11package coderd
22
33import (
4+ "archive/tar"
5+ "archive/zip"
6+ "bytes"
47 "crypto/sha256"
58 "database/sql"
69 "encoding/hex"
@@ -9,6 +12,7 @@ import (
912 "io"
1013 "net/http"
1114
15+ "cdr.dev/slog"
1216 "github.com/go-chi/chi/v5"
1317 "github.com/google/uuid"
1418
@@ -21,6 +25,9 @@ import (
2125
2226const (
2327 tarMimeType = "application/x-tar"
28+ zipMimeType = "application/zip"
29+
30+ httpFileMaxBytes = 10 * (10 << 20 )
2431)
2532
2633// @Summary Upload file
@@ -30,7 +37,7 @@ const (
3037// @Produce json
3138// @Accept application/x-tar
3239// @Tags Files
33- // @Param Content-Type header string true "Content-Type must be `application/x-tar`" default(application/x-tar)
40+ // @Param Content-Type header string true "Content-Type must be `application/x-tar` or `application/zip` " default(application/x-tar)
3441// @Param file formData file true "File to be uploaded"
3542// @Success 201 {object} codersdk.UploadResponse
3643// @Router /files [post]
@@ -39,17 +46,16 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
3946 apiKey := httpmw .APIKey (r )
4047
4148 contentType := r .Header .Get ("Content-Type" )
42-
4349 switch contentType {
44- case tarMimeType :
50+ case tarMimeType , zipMimeType :
4551 default :
4652 httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
4753 Message : fmt .Sprintf ("Unsupported content type header %q." , contentType ),
4854 })
4955 return
5056 }
5157
52- r .Body = http .MaxBytesReader (rw , r .Body , 10 * ( 10 << 20 ) )
58+ r .Body = http .MaxBytesReader (rw , r .Body , httpFileMaxBytes )
5359 data , err := io .ReadAll (r .Body )
5460 if err != nil {
5561 httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
@@ -58,6 +64,28 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
5864 })
5965 return
6066 }
67+
68+ if contentType == zipMimeType {
69+ zipReader , err := zip .NewReader (bytes .NewReader (data ), int64 (len (data )))
70+ if err != nil {
71+ httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
72+ Message : "Incomplete .zip archive file." ,
73+ Detail : err .Error (),
74+ })
75+ return
76+ }
77+
78+ data , err = CreateTarFromZip (zipReader )
79+ if err != nil {
80+ httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
81+ Message : "Internal error processing .zip archive." ,
82+ Detail : err .Error (),
83+ })
84+ return
85+ }
86+ contentType = tarMimeType
87+ }
88+
6189 hashBytes := sha256 .Sum256 (data )
6290 hash := hex .EncodeToString (hashBytes [:])
6391 file , err := api .Database .GetFileByHashAndCreator (ctx , database.GetFileByHashAndCreatorParams {
@@ -108,7 +136,10 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
108136// @Success 200
109137// @Router /files/{fileID} [get]
110138func (api * API ) fileByID (rw http.ResponseWriter , r * http.Request ) {
111- ctx := r .Context ()
139+ var (
140+ ctx = r .Context ()
141+ format = r .URL .Query ().Get ("format" )
142+ )
112143
113144 fileID := chi .URLParam (r , "fileID" )
114145 if fileID == "" {
@@ -139,7 +170,29 @@ func (api *API) fileByID(rw http.ResponseWriter, r *http.Request) {
139170 return
140171 }
141172
142- rw .Header ().Set ("Content-Type" , file .Mimetype )
143- rw .WriteHeader (http .StatusOK )
144- _ , _ = rw .Write (file .Data )
173+ switch format {
174+ case codersdk .FormatZip :
175+ if file .Mimetype != codersdk .ContentTypeTar {
176+ httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
177+ Message : "Only .tar files can be converted to .zip format" ,
178+ Detail : err .Error (),
179+ })
180+ return
181+ }
182+
183+ rw .Header ().Set ("Content-Type" , codersdk .ContentTypeZip )
184+ rw .WriteHeader (http .StatusOK )
185+ err = WriteZipArchive (rw , tar .NewReader (bytes .NewReader (file .Data )))
186+ if err != nil {
187+ api .Logger .Error (ctx , "invalid .zip archive" , slog .F ("file_id" , fileID ), slog .F ("mimetype" , file .Mimetype ), slog .Error (err ))
188+ }
189+ case "" : // no format? no conversion
190+ rw .Header ().Set ("Content-Type" , file .Mimetype )
191+ _ , _ = rw .Write (file .Data )
192+ default :
193+ httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
194+ Message : "Unsupported conversion format." ,
195+ Detail : err .Error (),
196+ })
197+ }
145198}
0 commit comments