diff --git a/cms/Dockerfile b/cms/Dockerfile index 7b1abb7..ba8444d 100644 --- a/cms/Dockerfile +++ b/cms/Dockerfile @@ -14,7 +14,6 @@ RUN apk add --no-cache ca-certificates WORKDIR /app COPY --from=builder /out/cms ./cms -COPY static ./static EXPOSE 8081 ENTRYPOINT ["./cms"] diff --git a/cms/go.mod b/cms/go.mod index 254c48d..9bf06ba 100644 --- a/cms/go.mod +++ b/cms/go.mod @@ -3,13 +3,11 @@ module thamanyah/cms/v2 go 1.25.12 require ( - github.com/a-h/templ v0.3.1020 github.com/aws/aws-sdk-go-v2 v1.43.5 github.com/aws/aws-sdk-go-v2/config v1.32.36 github.com/aws/aws-sdk-go-v2/service/mediaconvert v1.97.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.107.1 github.com/golang-migrate/migrate/v4 v4.19.1 - github.com/google/uuid v1.6.0 github.com/lib/pq v1.12.3 ) @@ -29,4 +27,5 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.45.5 // indirect github.com/aws/smithy-go v1.27.7 // indirect + golang.org/x/sys v0.41.0 // indirect ) diff --git a/cms/go.sum b/cms/go.sum index 52808f9..d6d2c05 100644 --- a/cms/go.sum +++ b/cms/go.sum @@ -2,8 +2,6 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw= -github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM= github.com/aws/aws-sdk-go-v2 v1.43.5 h1:yKT5GYnFWhuDo+DqKvE5ZPwVn3RjC4MAeBtZGlh6AVM= github.com/aws/aws-sdk-go-v2 v1.43.5/go.mod h1:wZjAJppCntyOGgVSmgVTfDyRJK5PHOasO6Wsy8U7Axk= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.17 h1:mn+Vxb9zgz/FE/yDTcFim3DZ1qpcrxR+qBQkBrl6bzA= @@ -68,10 +66,6 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA= github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= diff --git a/cms/internal/handlers/handlers.go b/cms/internal/handlers/handlers.go index f0bd21e..5d37ca0 100644 --- a/cms/internal/handlers/handlers.go +++ b/cms/internal/handlers/handlers.go @@ -1,20 +1,23 @@ package handlers import ( + "encoding/json" + "log" "net/http" - - "thamanyah/cms/v2/internal/views" ) -func Home(w http.ResponseWriter, r *http.Request) { - views.Home().Render(r.Context(), w) -} - -func Hello(w http.ResponseWriter, r *http.Request) { - views.Greeting("Fahd Shalhoub").Render(r.Context(), w) -} - func Health(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.Write([]byte("ok")) + writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) +} + +func writeJSON(w http.ResponseWriter, status int, body any) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + if err := json.NewEncoder(w).Encode(body); err != nil { + log.Printf("Something Went Wrong Writing The Response: %s", err) + } +} + +func writeJSONError(w http.ResponseWriter, status int, message string) { + writeJSON(w, status, map[string]string{"error": message}) } diff --git a/cms/internal/handlers/videos.go b/cms/internal/handlers/videos.go index f8cecdb..e409e7f 100644 --- a/cms/internal/handlers/videos.go +++ b/cms/internal/handlers/videos.go @@ -10,7 +10,6 @@ import ( "path/filepath" "strings" "thamanyah/cms/v2/internal/services" - "thamanyah/cms/v2/internal/views" "time" ) @@ -20,15 +19,22 @@ const ( maxJSONBodySize = 1 << 20 // 1 MiB ) -func NewVideo(w http.ResponseWriter, r *http.Request) { +type categoriesResponse struct { + Categories []string `json:"categories"` +} + +func ListCategories(w http.ResponseWriter, r *http.Request) { categories, err := services.DB.ListCategories(r.Context()) if err != nil { log.Printf("Something Went Wrong Loading Categories: %s", err) - http.Error(w, "Something Went Wrong Loading Categories", http.StatusInternalServerError) + writeJSONError(w, http.StatusInternalServerError, "Something Went Wrong Loading Categories") return } - views.VideoUpload(categories).Render(r.Context(), w) + if categories == nil { + categories = []string{} + } + writeJSON(w, http.StatusOK, categoriesResponse{Categories: categories}) } type presignRequest struct { @@ -68,8 +74,7 @@ func PresignVideoUpload(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(presignResponse{UploadURL: presignedURL, Key: key}) + writeJSON(w, http.StatusOK, presignResponse{UploadURL: presignedURL, Key: key}) } type completeRequest struct { @@ -81,24 +86,39 @@ type completeRequest struct { Key string `json:"key"` } +type videoResponse struct { + ID string `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Categories []string `json:"categories"` + Tags string `json:"tags"` + FileName string `json:"fileName"` + StorageKey string `json:"storageKey"` + MediaConvertJobID string `json:"mediaConvertJobId"` + Status string `json:"status"` + SizeBytes int64 `json:"sizeBytes"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + func CompleteVideoUpload(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxJSONBodySize) var req completeRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - renderUploadError(w, r, "Invalid request.") + writeJSONError(w, http.StatusBadRequest, "Invalid request.") return } title := strings.TrimSpace(req.Title) if title == "" { - renderUploadError(w, r, "Title is required.") + writeJSONError(w, http.StatusUnprocessableEntity, "Title is required.") return } key := strings.TrimSpace(req.Key) if key == "" || !strings.HasPrefix(key, videoUploadPrefix+"/") { - renderUploadError(w, r, "A video file is required.") + writeJSONError(w, http.StatusUnprocessableEntity, "A video file is required.") return } @@ -112,11 +132,11 @@ func CompleteVideoUpload(w http.ResponseWriter, r *http.Request) { jobID, err := services.MediaConvertClient.QueueEncodingJob(r.Context(), key) if err != nil { log.Printf("Something Went Wrong On Creation Of Transcoding Job: %s", err) - renderUploadError(w, r, "Something Went Wrong On Creation Of Transcoding Job") + writeJSONError(w, http.StatusInternalServerError, "Something Went Wrong On Creation Of Transcoding Job") return } - _, err = services.DB.CreateVideo(r.Context(), services.Video{ + video, err := services.DB.CreateVideo(r.Context(), services.Video{ Title: title, Description: strings.TrimSpace(req.Description), Categories: categories, @@ -129,32 +149,24 @@ func CompleteVideoUpload(w http.ResponseWriter, r *http.Request) { }) if err != nil { log.Printf("Something Went Wrong Saving The Video Record: %s", err) - renderUploadError(w, r, "Something Went Wrong Saving The Video Record") + writeJSONError(w, http.StatusInternalServerError, "Something Went Wrong Saving The Video Record") return } - views.VideoUploadSuccess(views.VideoMetadata{ - Title: title, - Description: strings.TrimSpace(req.Description), - Categories: categories, - Tags: strings.TrimSpace(req.Tags), - FileName: strings.TrimSpace(req.FileName), - JobID: jobID, - StoredAs: key, - SizeBytes: 60, - }).Render(r.Context(), w) -} - -func writeJSONError(w http.ResponseWriter, status int, message string) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(status) - json.NewEncoder(w).Encode(map[string]string{"error": message}) -} - -func renderUploadError(w http.ResponseWriter, r *http.Request, message string) { - fmt.Println("Error Ocurred: " + message) - w.WriteHeader(http.StatusUnprocessableEntity) - views.VideoUploadError(message).Render(r.Context(), w) + writeJSON(w, http.StatusCreated, videoResponse{ + ID: video.ID, + Title: video.Title, + Description: video.Description, + Categories: video.Categories, + Tags: video.Tags, + FileName: video.FileName, + StorageKey: video.StorageKey, + MediaConvertJobID: video.MediaConvertJobID, + Status: video.Status, + SizeBytes: video.SizeBytes, + CreatedAt: video.CreatedAt, + UpdatedAt: video.UpdatedAt, + }) } func randomFilename(original string) (string, error) { diff --git a/cms/internal/views/home.templ b/cms/internal/views/home.templ deleted file mode 100644 index db4d5dd..0000000 --- a/cms/internal/views/home.templ +++ /dev/null @@ -1,19 +0,0 @@ -package views - -import "thamanyah/cms/v2/internal/views/layouts" - -templ Home() { - @layouts.Layout("Thamanyah CMS") { -

Thamanyah CMS

-

htmx is wired up and ready to go.

- -
-

Upload a video

- } -} - -templ Greeting(name string) { -

Hello, { name }! This fragment was swapped in by htmx.

-} diff --git a/cms/internal/views/home_templ.go b/cms/internal/views/home_templ.go deleted file mode 100644 index 3f1437f..0000000 --- a/cms/internal/views/home_templ.go +++ /dev/null @@ -1,102 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.3.1020 -package views - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -import "thamanyah/cms/v2/internal/views/layouts" - -func Home() templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

Thamanyah CMS

htmx is wired up and ready to go.

Upload a video

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) - templ_7745c5c3_Err = layouts.Layout("Thamanyah CMS").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func Greeting(name string) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var3 := templ.GetChildren(ctx) - if templ_7745c5c3_Var3 == nil { - templ_7745c5c3_Var3 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Hello, ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(name) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/home.templ`, Line: 18, Col: 17} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "! This fragment was swapped in by htmx.

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/cms/internal/views/layouts/layout.templ b/cms/internal/views/layouts/layout.templ deleted file mode 100644 index c32da78..0000000 --- a/cms/internal/views/layouts/layout.templ +++ /dev/null @@ -1,26 +0,0 @@ -package layouts - -templ Layout(title string) { - - - - - - { title } - - - - -
- { children... } -
- - - -} diff --git a/cms/internal/views/layouts/layout_templ.go b/cms/internal/views/layouts/layout_templ.go deleted file mode 100644 index 7b84c88..0000000 --- a/cms/internal/views/layouts/layout_templ.go +++ /dev/null @@ -1,61 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.3.1020 -package layouts - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -func Layout(title string) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var2 string - templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/layouts/layout.templ`, Line: 9, Col: 17} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/cms/internal/views/types.go b/cms/internal/views/types.go deleted file mode 100644 index 867beed..0000000 --- a/cms/internal/views/types.go +++ /dev/null @@ -1,34 +0,0 @@ -package views - -import ( - "fmt" - "strings" -) - -type VideoMetadata struct { - Title string - Description string - Categories []string - Tags string - FileName string - StoredAs string - JobID string - SizeBytes int64 -} - -func joinCategories(categories []string) string { - return strings.Join(categories, ", ") -} - -func formatSize(n int64) string { - const unit = 1024 - if n < unit { - return fmt.Sprintf("%d B", n) - } - div, exp := int64(unit), 0 - for m := n / unit; m >= unit; m /= unit { - div *= unit - exp++ - } - return fmt.Sprintf("%.1f %ciB", float64(n)/float64(div), "KMGTPE"[exp]) -} diff --git a/cms/internal/views/video.templ b/cms/internal/views/video.templ deleted file mode 100644 index 672a3c9..0000000 --- a/cms/internal/views/video.templ +++ /dev/null @@ -1,154 +0,0 @@ -package views - -import "thamanyah/cms/v2/internal/views/layouts" - -templ VideoUpload(categories []string) { - @layouts.Layout("Upload Video · Thamanyah CMS") { -

Upload a video

-

Provide the video file along with its metadata.

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- - -
-
- - } -} - -templ VideoUploadSuccess(v VideoMetadata) { -
-

Upload complete

-
-
Title
-
{ v.Title }
- if v.Description != "" { -
Description
-
{ v.Description }
- } -
Categories
-
{ joinCategories(v.Categories) }
-
Enqueue Job ID
-
{ v.JobID }
- if v.Tags != "" { -
Tags
-
{ v.Tags }
- } -
File
-
- { v.FileName } ({ formatSize(v.SizeBytes) }) -
-
-
-} - -templ VideoUploadError(message string) { -
-

{ message }

-
-} diff --git a/cms/internal/views/video_templ.go b/cms/internal/views/video_templ.go deleted file mode 100644 index 9fc84e8..0000000 --- a/cms/internal/views/video_templ.go +++ /dev/null @@ -1,278 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.3.1020 -package views - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -import "thamanyah/cms/v2/internal/views/layouts" - -func VideoUpload(categories []string) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

Upload a video

Provide the video file along with its metadata.

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) - templ_7745c5c3_Err = layouts.Layout("Upload Video · Thamanyah CMS").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func VideoUploadSuccess(v VideoMetadata) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "

Upload complete

Title
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var6 string - templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(v.Title) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 129, Col: 16} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if v.Description != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
Description
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var7 string - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(v.Description) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 132, Col: 23} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
Categories
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var8 string - templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(joinCategories(v.Categories)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 135, Col: 37} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
Enqueue Job ID
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var9 string - templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(v.JobID) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 137, Col: 16} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if v.Tags != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
Tags
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(v.Tags) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 140, Col: 16} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
File
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var11 string - templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(v.FileName) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 144, Col: 16} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " (") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(formatSize(v.SizeBytes)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 144, Col: 45} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, ")
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func VideoUploadError(message string) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var13 := templ.GetChildren(ctx) - if templ_7745c5c3_Var13 == nil { - templ_7745c5c3_Var13 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var14 string - templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(message) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/video.templ`, Line: 152, Col: 34} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/cms/main.go b/cms/main.go index 95764db..163f4bb 100644 --- a/cms/main.go +++ b/cms/main.go @@ -78,13 +78,10 @@ func runServer() { mux := http.NewServeMux() - mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) - mux.HandleFunc("GET /", handlers.Home) - mux.HandleFunc("GET /api/hello", handlers.Hello) mux.HandleFunc("GET /health", handlers.Health) - mux.HandleFunc("GET /videos/new", handlers.NewVideo) - mux.HandleFunc("POST /videos/presign", handlers.PresignVideoUpload) - mux.HandleFunc("POST /videos", handlers.CompleteVideoUpload) + mux.HandleFunc("GET /api/categories", handlers.ListCategories) + mux.HandleFunc("POST /api/videos/presign", handlers.PresignVideoUpload) + mux.HandleFunc("POST /api/videos", handlers.CompleteVideoUpload) addr := ":8081" log.Printf("listening on %s", addr) diff --git a/cms/static/css/app.css b/cms/static/css/app.css deleted file mode 100644 index 6de7e62..0000000 --- a/cms/static/css/app.css +++ /dev/null @@ -1,82 +0,0 @@ -:root { - color-scheme: light dark; - font-family: system-ui, sans-serif; -} - -body { - margin: 0; - padding: 2rem; -} - -.container { - max-width: 40rem; - margin: 0 auto; -} - -button { - padding: 0.5rem 1rem; - font-size: 1rem; - cursor: pointer; -} - -.field { - display: flex; - flex-direction: column; - gap: 0.25rem; - margin-bottom: 1rem; -} - -.field label { - font-weight: 600; -} - -.field input, -.field textarea, -.field select { - padding: 0.5rem; - font-size: 1rem; - font-family: inherit; -} - -progress { - display: block; - width: 100%; - margin-bottom: 1rem; -} - -.htmx-indicator { - opacity: 0; - transition: opacity 200ms ease-in; -} - -.htmx-request.htmx-indicator, -.htmx-request .htmx-indicator { - opacity: 1; -} - -.result { - margin-top: 1.5rem; - padding: 1rem; - border-radius: 0.25rem; - border: 1px solid; -} - -.result-success { - border-color: #2f9e44; -} - -.result-error { - border-color: #e03131; -} - -dl { - margin: 0; -} - -dt { - font-weight: 600; -} - -dd { - margin: 0 0 0.75rem 0; -}