from flask import Flask, render_template, abort, send_file, jsonify import os import json from typing import List import markdown as md from dataclasses import dataclass from dataclasses_json import DataClassJsonMixin app = Flask(__name__) @dataclass class ArtifactItem(DataClassJsonMixin): file: str description: str @dataclass class Artifact(DataClassJsonMixin): id: str date: str changelog: str artifacts: List[ArtifactItem] hash: str = None short_hash: str = None @dataclass class ProjectInfo(DataClassJsonMixin): name: str commit_url: str class Project: id: str info: ProjectInfo def get_artifacts(self) -> List[Artifact]: result = [] artifacts_path = os.path.join("../builds", self.id, "artifacts") artifact_folders = sorted([folder.path for folder in os.scandir( artifacts_path) if folder.is_dir()], reverse=True) for artifact_folder in artifact_folders: info_file_path = os.path.join(artifact_folder, "info.json") artifact = self._parse_artifact(info_file_path) if artifact: result.append(artifact) return result def get_lastest_artifact(self) -> Artifact: artifacts_path = os.path.join("../builds", self.id, "artifacts") artifact_folders = sorted([folder.path for folder in os.scandir( artifacts_path) if folder.is_dir()], reverse=True) if not artifact_folders: return None latest_artifact_folder = max(artifact_folders) info_file_path = os.path.join(latest_artifact_folder, "info.json") return self._parse_artifact(info_file_path) def get_artifact(self, artifact_id) -> Artifact: artifact_folder = os.path.join("../builds", self.id, "artifacts", artifact_id) if not os.path.isdir(artifact_folder): return None info_file_path = os.path.join(artifact_folder, "info.json") return self._parse_artifact(info_file_path) def _parse_artifact(self, info_file_path): if not os.path.exists(info_file_path): return None try: with open(info_file_path, "r", encoding="utf-8") as f: artifact = Artifact.from_json(f.read()) artifact.date = artifact.date.strip() return artifact except: return None def get_projects() -> List[Project]: result = [] projects = [f.path for f in os.scandir("../builds") if f.is_dir()] for project in projects: info_path = os.path.join(project, "info.json") if not os.path.exists(info_path): continue try: proj = Project() _, proj.id = os.path.split(project) with open(info_path, "r", encoding="utf-8") as f: proj.info = ProjectInfo.from_json(f.read()) result.append(proj) except: continue return result @app.route("/api/projects//artifacts/latest") def get_latest_artifact(project_id): projects = get_projects() selected_project = next( (project for project in projects if project.id == project_id), None) if not selected_project: return abort(404) artifact = selected_project.get_lastest_artifact() if not artifact: return abort(404) return jsonify(artifact) @app.route("/api/projects//artifacts/") def get_artifact_by_id(project_id, artifact_id): projects = get_projects() selected_project = next( (project for project in projects if project.id == project_id), None) if not selected_project: return abort(404) artifact = selected_project.get_artifact(artifact_id) if not artifact: return abort(404) return jsonify(artifact) @app.route("/projects///") def download_item(project_id, artifact_id, download_item): file_path = os.path.join("../builds", project_id, "artifacts", artifact_id, download_item) if not os.path.exists(file_path): return abort(404) return send_file(file_path) @app.route("/projects/") def display_project(project_id): projects = get_projects() selected_project = next( (project for project in projects if project.id == project_id), None) if not selected_project: return abort(404) info_path = os.path.join("../builds", selected_project.id, "info.md") readme = None try: if os.path.exists(info_path): with open(info_path, "r", encoding="utf-8") as f: readme = md.markdown(f.read()) except: readme = None artifacts = selected_project.get_artifacts() return render_template("project_view.html", projects=projects, selected_project=selected_project, readme=readme, artifacts=artifacts) @app.route("/") def index(): projects = get_projects() return render_template("main.html", projects=projects) if __name__ == "__main__": app.run(host='0.0.0.0')