@@ -43,6 +43,7 @@ type ListProjectsInput struct {
4343 Limit int `query:"limit" default:"20" doc:"Number of items per page"`
4444 Status string `query:"status" doc:"Filter by status (comma-separated: running,stopped,partially running)"`
4545 Updates string `query:"updates" doc:"Filter by update status (has_update, up_to_date, error, unknown)"`
46+ Archived string `query:"archived" doc:"Archived filter: 'true' (only archived), 'all' (include archived). Default excludes archived."`
4647}
4748
4849type ListProjectsOutput struct {
@@ -152,6 +153,24 @@ type RestartProjectOutput struct {
152153 Body base.ApiResponse [base.MessageResponse ]
153154}
154155
156+ type ArchiveProjectInput struct {
157+ EnvironmentID string `path:"id" doc:"Environment ID"`
158+ ProjectID string `path:"projectId" doc:"Project ID"`
159+ }
160+
161+ type ArchiveProjectOutput struct {
162+ Body base.ApiResponse [base.MessageResponse ]
163+ }
164+
165+ type UnarchiveProjectInput struct {
166+ EnvironmentID string `path:"id" doc:"Environment ID"`
167+ ProjectID string `path:"projectId" doc:"Project ID"`
168+ }
169+
170+ type UnarchiveProjectOutput struct {
171+ Body base.ApiResponse [base.MessageResponse ]
172+ }
173+
155174type PullProjectImagesInput struct {
156175 EnvironmentID string `path:"id" doc:"Environment ID"`
157176 ProjectID string `path:"projectId" doc:"Project ID"`
@@ -343,6 +362,32 @@ func RegisterProjects(api huma.API, projectService *services.ProjectService) {
343362 },
344363 }, h .RestartProject )
345364
365+ huma .Register (api , huma.Operation {
366+ OperationID : "archive-project" ,
367+ Method : http .MethodPost ,
368+ Path : "/environments/{id}/projects/{projectId}/archive" ,
369+ Summary : "Archive a project" ,
370+ Description : "Archive a stopped Docker Compose project" ,
371+ Tags : []string {"Projects" },
372+ Security : []map [string ][]string {
373+ {"BearerAuth" : {}},
374+ {"ApiKeyAuth" : {}},
375+ },
376+ }, h .ArchiveProject )
377+
378+ huma .Register (api , huma.Operation {
379+ OperationID : "unarchive-project" ,
380+ Method : http .MethodPost ,
381+ Path : "/environments/{id}/projects/{projectId}/unarchive" ,
382+ Summary : "Unarchive a project" ,
383+ Description : "Unarchive a Docker Compose project" ,
384+ Tags : []string {"Projects" },
385+ Security : []map [string ][]string {
386+ {"BearerAuth" : {}},
387+ {"ApiKeyAuth" : {}},
388+ },
389+ }, h .UnarchiveProject )
390+
346391 huma .Register (api , huma.Operation {
347392 OperationID : "pull-project-images" ,
348393 Method : http .MethodPost ,
@@ -383,6 +428,9 @@ func (h *ProjectHandler) ListProjects(ctx context.Context, input *ListProjectsIn
383428 if input .Updates != "" {
384429 filters ["updates" ] = input .Updates
385430 }
431+ if input .Archived != "" {
432+ filters ["archived" ] = input .Archived
433+ }
386434
387435 params := pagination.QueryParams {
388436 SearchQuery : pagination.SearchQuery {
@@ -432,7 +480,7 @@ func (h *ProjectHandler) GetProjectStatusCounts(ctx context.Context, input *GetP
432480 return nil , huma .Error500InternalServerError ("service not available" )
433481 }
434482
435- _ , running , stopped , total , err := h .projectService .GetProjectStatusCounts (ctx )
483+ _ , running , stopped , total , archived , err := h .projectService .GetProjectStatusCounts (ctx )
436484 if err != nil {
437485 return nil , huma .Error500InternalServerError ((& common.ProjectStatusCountsError {Err : err }).Error ())
438486 }
@@ -441,9 +489,10 @@ func (h *ProjectHandler) GetProjectStatusCounts(ctx context.Context, input *GetP
441489 Body : base.ApiResponse [project.StatusCounts ]{
442490 Success : true ,
443491 Data : project.StatusCounts {
444- RunningProjects : int (running ),
445- StoppedProjects : int (stopped ),
446- TotalProjects : int (total ),
492+ RunningProjects : int (running ),
493+ StoppedProjects : int (stopped ),
494+ TotalProjects : int (total ),
495+ ArchivedProjects : int (archived ),
447496 },
448497 },
449498 }, nil
@@ -507,6 +556,10 @@ func (h *ProjectHandler) DownProject(ctx context.Context, input *DownProjectInpu
507556 }
508557
509558 if err := h .projectService .DownProject (ctx , input .ProjectID , * user ); err != nil {
559+ var archivedErr * common.ProjectArchivedError
560+ if errors .As (err , & archivedErr ) {
561+ return nil , huma .Error400BadRequest ((& common.ProjectDownError {Err : err }).Error ())
562+ }
510563 return nil , huma .Error500InternalServerError ((& common.ProjectDownError {Err : err }).Error ())
511564 }
512565
@@ -547,6 +600,8 @@ func (h *ProjectHandler) CreateProject(ctx context.Context, input *CreateProject
547600 response .DirName = utils .DerefString (proj .DirName )
548601 response .RelativePath = h .projectService .GetProjectRelativePath (ctx , proj .Path )
549602 response .GitOpsManagedBy = proj .GitOpsManagedBy
603+ response .IsArchived = proj .IsArchived
604+ response .ArchivedAt = proj .ArchivedAt
550605
551606 return & CreateProjectOutput {
552607 Body : base.ApiResponse [project.CreateReponse ]{
@@ -632,6 +687,10 @@ func (h *ProjectHandler) RedeployProject(ctx context.Context, input *RedeployPro
632687 }
633688
634689 if err := h .projectService .RedeployProject (ctx , input .ProjectID , * user ); err != nil {
690+ var archivedErr * common.ProjectArchivedError
691+ if errors .As (err , & archivedErr ) {
692+ return nil , huma .Error400BadRequest (err .Error ())
693+ }
635694 return nil , huma .Error400BadRequest ((& common.ProjectRedeploymentError {Err : err }).Error ())
636695 }
637696
@@ -764,6 +823,10 @@ func (h *ProjectHandler) RestartProject(ctx context.Context, input *RestartProje
764823 }
765824
766825 if err := h .projectService .RestartProject (ctx , input .ProjectID , * user ); err != nil {
826+ var archivedErr * common.ProjectArchivedError
827+ if errors .As (err , & archivedErr ) {
828+ return nil , huma .Error400BadRequest (err .Error ())
829+ }
767830 return nil , huma .Error400BadRequest ((& common.ProjectRestartError {Err : err }).Error ())
768831 }
769832
@@ -777,6 +840,62 @@ func (h *ProjectHandler) RestartProject(ctx context.Context, input *RestartProje
777840 }, nil
778841}
779842
843+ func (h * ProjectHandler ) ArchiveProject (ctx context.Context , input * ArchiveProjectInput ) (* ArchiveProjectOutput , error ) {
844+ if h .projectService == nil {
845+ return nil , huma .Error500InternalServerError ("service not available" )
846+ }
847+
848+ if input .ProjectID == "" {
849+ return nil , huma .Error400BadRequest ((& common.ProjectIDRequiredError {}).Error ())
850+ }
851+
852+ user , exists := humamw .GetCurrentUserFromContext (ctx )
853+ if ! exists {
854+ return nil , huma .Error401Unauthorized ((& common.NotAuthenticatedError {}).Error ())
855+ }
856+
857+ if err := h .projectService .ArchiveProject (ctx , input .ProjectID , * user ); err != nil {
858+ var mustStopErr * common.ProjectMustBeStoppedError
859+ if errors .As (err , & mustStopErr ) {
860+ return nil , huma .Error400BadRequest (err .Error ())
861+ }
862+ return nil , huma .Error500InternalServerError ((& common.ProjectArchiveError {Err : err }).Error ())
863+ }
864+
865+ return & ArchiveProjectOutput {
866+ Body : base.ApiResponse [base.MessageResponse ]{
867+ Success : true ,
868+ Data : base.MessageResponse {Message : "Project archived successfully" },
869+ },
870+ }, nil
871+ }
872+
873+ func (h * ProjectHandler ) UnarchiveProject (ctx context.Context , input * UnarchiveProjectInput ) (* UnarchiveProjectOutput , error ) {
874+ if h .projectService == nil {
875+ return nil , huma .Error500InternalServerError ("service not available" )
876+ }
877+
878+ if input .ProjectID == "" {
879+ return nil , huma .Error400BadRequest ((& common.ProjectIDRequiredError {}).Error ())
880+ }
881+
882+ user , exists := humamw .GetCurrentUserFromContext (ctx )
883+ if ! exists {
884+ return nil , huma .Error401Unauthorized ((& common.NotAuthenticatedError {}).Error ())
885+ }
886+
887+ if err := h .projectService .UnarchiveProject (ctx , input .ProjectID , * user ); err != nil {
888+ return nil , huma .Error500InternalServerError ((& common.ProjectUnarchiveError {Err : err }).Error ())
889+ }
890+
891+ return & UnarchiveProjectOutput {
892+ Body : base.ApiResponse [base.MessageResponse ]{
893+ Success : true ,
894+ Data : base.MessageResponse {Message : "Project unarchived successfully" },
895+ },
896+ }, nil
897+ }
898+
780899// PullProjectImages pulls all images for a project with streaming progress.
781900func (h * ProjectHandler ) PullProjectImages (ctx context.Context , input * PullProjectImagesInput ) (* huma.StreamResponse , error ) {
782901 if h .projectService == nil {
0 commit comments