|  | @@ -1,4 +1,4 @@
 | 
	
		
			
				|  |  | -from flask import Flask, render_template, abort, send_file
 | 
	
		
			
				|  |  | +from flask import Flask, render_template, abort, send_file, jsonify
 | 
	
		
			
				|  |  |  import os
 | 
	
		
			
				|  |  |  import json
 | 
	
		
			
				|  |  |  from typing import List
 | 
	
	
		
			
				|  | @@ -33,6 +33,7 @@ class Project:
 | 
	
		
			
				|  |  |      id: str
 | 
	
		
			
				|  |  |      info: ProjectInfo
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def get_artifacts(self) -> List[Artifact]:
 | 
	
		
			
				|  |  |          result = []
 | 
	
		
			
				|  |  |          artifacts_path = os.path.join("../builds", self.id, "artifacts")
 | 
	
	
		
			
				|  | @@ -41,19 +42,44 @@ class Project:
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          for artifact_folder in artifact_folders:
 | 
	
		
			
				|  |  |              info_file_path = os.path.join(artifact_folder, "info.json")
 | 
	
		
			
				|  |  | -            if not os.path.exists(info_file_path):
 | 
	
		
			
				|  |  | -                continue
 | 
	
		
			
				|  |  | -            try:
 | 
	
		
			
				|  |  | -                with open(info_file_path, "r", encoding="utf-8") as f:
 | 
	
		
			
				|  |  | -                    artifact = Artifact.from_json(f.read())
 | 
	
		
			
				|  |  | -                    artifact.date = artifact.date.strip()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    result.append(artifact)
 | 
	
		
			
				|  |  | -            except:
 | 
	
		
			
				|  |  | -                continue
 | 
	
		
			
				|  |  | +            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()]
 | 
	
	
		
			
				|  | @@ -72,6 +98,38 @@ def get_projects() -> List[Project]:
 | 
	
		
			
				|  |  |      return result
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +@app.route("/api/projects/<string:project_id>/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/<string:project_id>/artifacts/<string:artifact_id>")
 | 
	
		
			
				|  |  | +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/<string:project_id>/<string:artifact_id>/<string:download_item>")
 | 
	
		
			
				|  |  |  def download_item(project_id, artifact_id, download_item):
 | 
	
		
			
				|  |  |      file_path = os.path.join("../builds", project_id,
 |