FIX: Changed Content Type From Static mp4 To Input
Build, Push and Deploy CMS / build-push-deploy (push) Successful in 2m18s

This commit is contained in:
FahdShalhoub
2026-08-16 23:56:21 +03:00
parent f26ea98b52
commit 4b719f712d
5 changed files with 104 additions and 35 deletions
+63 -16
View File
@@ -28,11 +28,23 @@ go vet ./...
```
There are no test files in this repo (`cms`, `discovery`, or `infrastructure`) — don't assume a test suite exists.
Views are written as `.templ` files (github.com/a-h/templ) and compiled to
`*_templ.go`. If you edit a `.templ` file, regenerate its Go code with the
`templ generate` CLI before building (not installed in this environment by
default — install via `go install github.com/a-h/templ/cmd/templ@v0.3.1020`
to match `go.mod`, or check for an existing binary first).
`cms` is a JSON API only — it serves no HTML and has no static assets. The
OpenAPI spec is generated from swaggo annotations on the handlers into
`cms/docs`, a committed, compiled-in Go package. If you add or change a
handler, its annotation comments, or a request/response struct, regenerate it:
```bash
go install github.com/swaggo/swag/cmd/swag@v1.16.6 # match go.mod; not installed by default
swag init --generalInfo main.go --dir ./ --parseInternal --output ./docs
```
`--parseInternal` is required — the handlers live under `internal/`, which
swag skips without it. Keep the `swaggo/swag` version in `go.mod` and the CLI
in lockstep: `http-swagger/v2` transitively pulls a much older `swag` whose
`swag.Spec` struct lacks the `LeftDelim`/`RightDelim` fields newer generators
emit, and the build breaks outright if the two drift. (The CLI's `--version`
misreports itself as v1.16.4; `go version -m $(go env GOPATH)/bin/swag` gives
the real one.)
### infrastructure (Go, Pulumi, module `thamanyah`)
```bash
@@ -70,12 +82,29 @@ S3/MediaConvert or start the server. This is run as its own ECS container
before the main container starts (see infrastructure below), so `runServer`
never runs migrations itself, only `AssertSuccessfulConnection`.
Routes (`cms/main.go`): `GET /`, `GET /health`, `GET /videos/new`,
`POST /videos/presign`, `POST /videos`, static files under `/static/`.
Routes (`cms/main.go`): `GET /health`, `GET /api/categories`,
`POST /api/videos/presign`, `POST /api/videos`, plus Swagger UI at
`GET /swagger/` (`/swagger/doc.json` serves the spec). The UI assets are
embedded in the binary by `swaggo/files`, so nothing is read from disk and
nothing is fetched from a CDN at runtime.
Views live in `cms/internal/views` (templ components) with a shared
`layouts.Layout` wrapper; `types.go` holds view-model structs like
`VideoMetadata` used by the upload-success page.
Handlers live in `cms/internal/handlers``handlers.go` holds `Health` and
the shared response writers, `videos.go` the categories and upload endpoints.
There is no view layer: the `internal/views` templ package, the `static/`
directory, and the htmx frontend were all removed when the service became a
JSON API, along with the `templ` dependency.
Error responses are RFC 9457 Problem Details objects
(`application/problem+json`), written by
`writeProblem(w, status, title, detail)`. `type` is always `"about:blank"`;
`title` is a short summary held identical across every occurrence of a given
problem, so clients can branch on it; `detail` is the only member that varies
with request data. Success responses go through `writeJSON`
(`application/json`). Both share `writeJSONContent`. Note this deviates
slightly from RFC 9457, which pairs an `about:blank` type with a title that is
just the HTTP status phrase — meaningful titles like these are supposed to
carry a real `type` URI. Adding per-problem type URIs is the conforming fix if
it ever matters.
### Data model (Postgres, `cms/internal/db/migrations/`)
- `videos` — one row per uploaded video: `title`, `description`, `tags`
@@ -95,11 +124,12 @@ Views live in `cms/internal/views` (templ components) with a shared
and its `video_categories` links inside a single transaction.
### Video upload → transcode pipeline
1. Browser calls `POST /videos/presign` → cms returns a presigned S3 `PUT`
1. Client calls `POST /api/videos/presign` → cms returns a presigned S3 `PUT`
URL for `raw-uploads-bucket`, key `videos/<random-hex>.<ext>`.
2. Browser `PUT`s the file directly to S3 (requires the bucket's CORS rule,
set up in `infrastructure/main.go`).
3. Browser calls `POST /videos` with the metadata + key → cms calls
2. Client `PUT`s the file directly to S3 (from a browser this requires the
bucket's CORS rule, set up in `infrastructure/main.go` — see Known gaps,
its allowed origin is now stale). The file never passes through cms.
3. Client calls `POST /api/videos` with the metadata + key → cms calls
`MediaConvertClient.QueueEncodingJob(key)`, submitting a MediaConvert job
`s3://raw-uploads-bucket/<key>``s3://encoded-bucket/<key>` (H.264/AAC →
MP4, QVBR rate control — QVBR requires `MaxBitrate` to be set explicitly),
@@ -142,8 +172,9 @@ on boot if any required var is empty.
- **S3 raw uploads**: `raw-uploads-bucket` is a separate, private bucket for
pre-transcode uploads — deliberately kept apart from `encoded-bucket` so
raw source video is never reachable through the public CDN. CORS is
scoped to `PUT` only, from the `cms` ALB's own origin (browser uploads
directly via presigned URL).
scoped to `PUT` only, from the `cms` ALB's own origin — correct back when
cms served the upload page itself, but stale now that it serves no UI (see
Known gaps).
- **IAM roles** — four distinct roles/users, each scoped narrowly, don't
conflate them:
- `ecs-task-execution-role` — shared by both services' ECS *agent* (image
@@ -193,3 +224,19 @@ on boot if any required var is empty.
- `discovery` has infra provisioned (ECR repo, ECS service, ALB, Postgres
DB/role) but no application code — it doesn't touch its database at all.
- No automated tests exist for `cms`, `discovery`, or `infrastructure`.
- **No browser client can reach the API yet.** Two separate CORS gaps, both
left over from cms dropping its own UI: cms sends no CORS headers of its
own, and `raw-uploads-bucket`'s CORS rule in `infrastructure/main.go` still
allows only the `cms` ALB origin, so a frontend served from anywhere else
fails preflight on the direct S3 `PUT`. Both need the real frontend origin
before a browser client works end to end.
- `videos.size_bytes` is hardcoded to `60` in `CompleteVideoUpload` — the
request body carries no size field, so every row stores 60 and the API
hands that back in `sizeBytes`. Fixing it means adding `sizeBytes` to the
`POST /api/videos` request contract.
- The generated spec is Swagger 2.0, which has no per-response media type, so
error responses are documented as `application/json` even though they are
actually sent as `application/problem+json`. The schemas themselves are
right. `swag init --v3.1` would emit OpenAPI 3.1 and resolve it.
- `cms/uploads/` holds a stray `.mov` from an older local-disk upload path.
It is untracked, unreferenced by any code, and safe to delete.