Commit Graph
100 Commits
Author SHA1 Message Date
yusufipk 4e26da62bd test(e2e): derive the storage route glob from R2_ENDPOINT
`failure-recovery.spec.ts` hardcoded `http://minio-test:9000/**`, which is the
compose hostname. CI publishes MinIO on localhost, so the pattern matched
nothing there: the PUT went through, the upload succeeded, and the test sat
waiting for an error message that was never going to appear. It passed locally
and failed on CI for a reason the diagnostic did not name.

The glob now comes from R2_ENDPOINT, and the test counts the PUTs it actually
refused and asserts the count is not zero. A pattern that matches nothing is now
a failure that says so, rather than a failure that blames the error message.

Recorded in AGENTS.md as the third way a test can be worthless, alongside a
note to run a new spec under CI conditions and not only locally.
2026-07-26 15:01:00 +07:00
yusufipk 0187db5dc7 test: close the coverage gaps the first round left
Second pass over the suite, driven by the inventory in the gaps document. Nine
agents wrote suites in parallel against private databases, then a tenth read all
of it adversarially and five of its findings were fixed.

  unit + component  2076 -> 2079 (+888 over the round)
  api                647 -> 1015
  e2e                 18 -> 29

What was closed:

- lib/route-access.ts, the page-level authorization layer, went from zero tests
  to 48. Every API route was guarded and none of the pages were.
- The five media proxy routes now have a real 2xx beside every 403. The blocker
  was the positive control, solved by stubbing r2Client.send() and leaving
  lib/r2-media-proxy.ts itself real.
- Every remaining server-side lib module: invitations, email verification, the
  upload tokens, the logger, request origin, the whole R2 and Bunny lifecycle,
  notifications and admin stats.
- Six video-page hooks, and the chunking arithmetic extracted out of
  lib/client/r2-video-upload.ts as a pure module.
- Five end-to-end flows: workspace members, bulk operations, the admin area,
  player interaction and failure recovery.

Three things about the harness itself turned out to be wrong:

- Two @/lib/r2 stubs in tests/setup/api.ts had the wrong return shape, so every
  route reaching finalizeR2VideoUpload silently took the "not a valid video"
  branch and no test noticed.
- The auth matrix asserted only "not 2xx", which two entries satisfied without
  their guard existing. It now requires 401 or 403, which makes both
  load-bearing, and all 60 routes pass the stricter form.
- Both admin API routes had no positive control anywhere: replacing their guard
  with an unconditional refusal left the entire suite green. Found by the
  adversarial review, now covered.

Process:

- bun run test:mutation runs StrykerJS over the authorization and validation
  modules. Diagnostic, not a gate, weekly in CI rather than on a push.
- playwright.config.ts gains an opt-in webkit project for the player spec.
- AGENTS.md now requires a batch of new tests to be reviewed by somebody who
  did not write them.

Only two production files change, both deliberate: lib/auth.ts loses a verbatim
copy of its own permission formulas, and lib/client/r2-video-upload.ts calls the
extracted arithmetic. No behaviour change in either.
2026-07-26 13:25:11 +07:00
yusufipk fe42c0836f fix(test): stop anchoring the post-register URL assertion
The login page derives callbackUrl from its own default when the
parameter is absent, and on CI it arrives as
/login?registered=true&callbackUrl=%2Fdashboard. Anchoring the pattern
with $ made that a deterministic CI-only failure while the suite passed
locally on every run, including with CI=1.

What the register flow promises is the login page plus registered=true.
The rest of the query string is not part of that contract, so the
pattern now tolerates extra parameters in any order.

The page snapshot that diagnosed this also turned up a product bug,
recorded in the findings notes rather than fixed here: the success
banner tells every new user to check their email for a verification
link, including in the self-hosted default where SMTP is unset, email
verification is off, and the account is already usable.
2026-07-26 11:41:05 +07:00
yusufipk 72159377fb fix(ci): make the suites run under node, and start MinIO as a step
Both failures were environment-specific and invisible locally.

The vitest projects only ever ran under bun here, because the containers
used for local runs have no node at all. On a GitHub runner the vitest
bin's `#!/usr/bin/env node` shebang wins, and node's ESM resolver cannot
resolve the extensionless 'next/server' that next-auth/lib/env.js
imports, so all 17 api suites died with ERR_MODULE_NOT_FOUND. The
next-auth inline rule that the unit project already carried is now
declared at the root so every project inherits it. All three projects
verified under node as well as bun.

The e2e job could never start: a GitHub Actions `services:` block cannot
pass a command to its container, and the MinIO entrypoint requires
`server /data`, so the container printed its usage text and exited.
MinIO now starts as a step with `docker run`, which means the job can no
longer run inside a container, which in turn removes the reason the
Playwright image was needed at all. The browser is installed on the
runner instead, so the image tag no longer has to be kept in lockstep
with the npm package.
2026-07-26 11:28:56 +07:00
yusufipk 1d099c68f2 test: add unit, API, component and end-to-end test suites
The repo had no automated tests. Every change was verified by hand.

Adds four layers, 2023 tests in total, runnable with one command:

- 1191 unit tests over the pure logic in lib/, including the full
  computeProjectAccess permission matrix and the billing gate
- 167 component and hook tests in jsdom, covering the hooks that hold
  real logic rather than presentational wrappers
- 647 API integration tests against a real Postgres, with only auth()
  mocked, including a data-driven sweep asserting that none of the 60
  route modules answers 2xx to an unauthenticated caller
- 18 Playwright specs driving a real browser against a real build

Infrastructure: vitest.config.ts with three projects, a disposable
Postgres and MinIO in docker-compose.test.yml, factories and helpers
under tests/, scripts/test.sh as the single entry point, a pre-push
hook running bun run verify, and CI split into check, test and e2e jobs.

The test database is built with prisma db push plus a replay of the
hand-written SQL, because prisma migrate deploy cannot build this schema
from empty: the migration history has no captured baseline. This mirrors
what scripts/docker-db-bootstrap.ts already does in production, and
tests/setup/db-global.ts carries a drift guard so a new migration fails
the run until someone reviews it.

Production code is unchanged apart from one pure-function extraction out
of use-video-player.ts, which was too large to test in jsdom.

Several tests pin behaviour that looks wrong, each marked KNOWN BUG in
place. TESTING.md section 12 records where the plan turned out to be
wrong, and AGENTS.md now states which layer a change needs a test in.
2026-07-26 11:17:26 +07:00
Yusuf İpek 52b2c8d2a9 Merge pull request #41 from yusufipk/dependabot/npm_and_yarn/npm_and_yarn-1c4f37dfd6
chore(deps): bump next from 16.2.6 to 16.2.11 in the npm_and_yarn group across 1 directory
2026-07-25 13:31:59 +03:00
yusufipk f9ac7fb089 chore(deps): sync bun.lock for next 16.2.11
Dependabot only bumped package.json; regenerate the lockfile so the manifest and bun.lock agree.
2026-07-25 17:26:57 +07:00
Yusuf İpek bad64d6d48 Merge pull request #40 from yusufipk/worktree-dependency
fix(deps): bump sharp to 0.35.3 for the libvips CVE fixes
2026-07-25 13:19:39 +03:00
Yusuf İpek 322395551a Merge pull request #38 from yusufipk/fix/public-project-hides-workspace-admin-actions
fix(auth): keep workspace admin permissions on public projects
2026-07-25 13:18:38 +03:00
yusufipk bac6af0ded fix(deps): bump sharp to 0.35.3 for the libvips CVE fixes
sharp < 0.35.0 ships libvips 1.2.4, which carries CVE-2026-33327,
CVE-2026-33328, CVE-2026-35590 and CVE-2026-35591 (Dependabot #21).
0.35.3 bundles libvips 1.3.2 (8.18.3).

next 16.2.6 still declares sharp ^0.34.5 as an optional dependency, so a
plain bump left a nested vulnerable copy under node_modules/next that the
image optimizer would resolve first. The overrides entry pins a single
sharp across the tree; it can go once next ships >= 16.3 with sharp ^0.35.
2026-07-25 17:15:30 +07:00
Yusuf İpek 58910a5f5a Merge pull request #39 from yusufipk/worktree-frame-counter
feat(player): add frame counter when scrubbing and seeking
2026-07-25 13:10:11 +03:00
yusufipk b23f3de666 feat(player): add frame counter when scrubbing and seeking
Show a timecode + frame readout above the timeline while dragging the
playhead, and flash it for a moment on keyboard/button seeks so frame
stepping is visible too.

Position and text are written from the existing rAF/DOM path that drives
the playhead, so the readout stays smooth without extra React renders.

Two supporting fixes the count depends on:

- Seed the frame rate from the HLS manifest FRAME-RATE attribute so a
  frame number is available before playback ever starts; previously the
  rate was only ever measured from requestVideoFrameCallback and stayed
  null until the video had played.
- Snap the measured rate to the nearest broadcast standard and skip
  samples taken mid-seek. A drifting float slid the count by whole
  frames late in a long video, and re-publishing a slightly different
  float on every presented frame forced a re-render per video frame.
2026-07-25 17:07:34 +07:00
Yusuf İpek 81285681dc Merge pull request #37 from yusufipk/worktree-fix-download-notice
feat(downloads): let the download progress toast be minimized
2026-07-25 12:59:18 +03:00
yusufipk 60b2bc7369 fix(auth): keep workspace admin permissions on public projects
checkProjectAccess skipped the workspace membership lookup whenever access
was already granted another way — a PUBLIC project, or an existing project
membership — and only forced it for intents other than 'view'. The workspace
role does not just gate entry though; it feeds canEdit/isWorkspaceMember.

So a workspace ADMIN who is not the project owner lost canEdit the moment a
project was switched to public: the Add Version item on video cards, plus
canManageTags/canResolveComments/canRequestApproval/canShareVideo on the
video page, all disappeared, and the approvals endpoint returned 403. The
underlying POST routes use intent 'manage' and would still have accepted the
write, so the permission was there — only the UI was gone.

Resolve the workspace role for every signed-in non-owner. Owners already pass
every check on their own, so theirs is still only loaded when they mutate.
2026-07-25 16:58:17 +07:00
yusufipk 481728b93d feat(downloads): let the download progress toast be minimized
The download progress toast sits in the bottom-right corner on top of the
comment composer, blocking the voice-recording button and the comment box for
the whole duration of a download.

Render it through toast.custom so it can be collapsed to a small pill (percent
+ spinner) and expanded again while the download keeps running. The minimized
choice sticks for the rest of the session. The sonner <li> is click-through, so
only the panel itself covers the controls underneath.

Also dismiss the panel on failure — it had duration: Infinity and used to stay
on screen forever after an error.
2026-07-25 16:56:29 +07:00
Yusuf İpek aeee1fc68b Merge pull request #31 from eehkay/fix/compare-r2-playback
fix: play r2 direct uploads in the compare versions view
2026-07-25 12:54:20 +03:00
yusufipk 2bad0a249f refactor(video): share R2 playback URL resolution and guard drift resync
- move resolveR2PlaybackUrl into lib/video-upload-validation.ts so the compare
  view and the main video page cannot drift apart
- validate the resolved URL with isPlayableVideoUrl before it reaches <video src>
- add a per-player cooldown so a follower that cannot keep up is not seeked
  every second, which would stutter rather than correct
2026-07-25 16:48:39 +07:00
Yusuf İpek 63e467f437 Merge pull request #26 from eehkay/fix/json-ld-scripts
fix: emit one JSON-LD script per schema object
2026-07-25 11:28:40 +03:00
Yusuf İpek b50ef39329 Merge pull request #36 from yusufipk/worktree-admin-panel-filtering
feat(admin): add search and status filters to the users table
2026-07-25 11:16:58 +03:00
Yusuf İpek a14eb9fb84 Merge pull request #34 from yusufipk/fix/bigint-safe-success-response
fix(api): serialize BigInt in all API success responses
2026-07-25 11:15:27 +03:00
yusufipk f64c04b271 feat(admin): add search and status filters to the users table
The users table could only be sorted, so finding a single account or
reviewing everyone in a given billing state meant paging through the
whole list.

Add three filters that compose with each other and with sorting:
- q: case-insensitive name/email search, submitted as a plain GET form
- status: one button per BillingSubscriptionStatus (active, canceled, ...)
- access: real in-app access, including collaborators on a paying
  owner's workspace or project

Resolving that collaborator access per user meant two queries per row.
Replace it with getCollaboratorAccessUserIds, which resolves every user
in two queries total and now backs both the column and the new filter.
2026-07-25 15:15:01 +07:00
Yusuf İpek b912c1767b Merge pull request #35 from yusufipk/fix/verify-email-redirect-origin
fix(auth): build verify-email redirects from the configured public or…
2026-07-25 10:59:52 +03:00
yusufipk 5871d4d87d fix(auth): build verify-email redirects from the configured public origin
Redirects were built relative to `request.url`, which behind a reverse proxy
resolves to the container-internal address. Verification succeeded but the
browser was sent to localhost:3000, so users saw a connection error instead of
the "email verified" confirmation.

Add getPublicOrigin() (NEXTAUTH_URL, then NEXT_PUBLIC_APP_URL, falling back to
the request origin for local development) and use it for every verify-email
redirect. The legacy GET redirect in the watch session route had the same
defect and is fixed alongside it.
2026-07-25 14:57:57 +07:00
yusufipk 0faa4b4e2a fix(billing): prevent duplicate subscriptions and make webhook sync authoritative
A Stripe customer can own several subscriptions. Two defects let that happen
and corrupt the user's billing state:

1. Checkout allowed a fresh subscription whenever the user was not ACTIVE/
   TRIALING, so a PAST_DUE user started a brand-new subscription (Stripe
   Checkout always creates one) instead of recovering the existing one.
   Add hasRecoverableSubscription() (ACTIVE/TRIALING/PAST_DUE/UNPAID/
   INCOMPLETE); block checkout and route these users to the billing portal
   ('Update Payment Method') both in the API guard and the settings UI.

2. Subscription webhooks trusted the event's single subscription, so an old
   subscription's deletion could clobber a newer active one (marking the user
   CANCELED / No access). Every subscription event now re-derives state from
   the full set of the customer's Stripe subscriptions via
   syncStripeCustomerSubscriptions() + selectAuthoritativeSubscription(),
   making the sync order-independent and self-healing.
2026-07-25 14:22:32 +07:00
yusufipk b5fd73dcf2 feat(admin): show subscription status and real access in user list
Add a Subscription column to the admin user listing showing each user's
billing status (Active, Trialing, Past due, Canceled, etc.) as a badge,
plus an effective-access indicator. Access reflects real in-app access,
not just the user's own subscription: collaborators on a paying owner's
workspace/project are shown as having access 'via team' (mirrors
hasAppNavigationAccess in lib/route-access.ts). Canceled-but-not-yet-
expired and trialing users are surfaced with their access-until date.
Column is DB-sortable and gated behind isStripeBillingEnabled().
2026-07-25 13:24:55 +07:00
Yusuf İpek ae78e97fde Merge pull request #25 from eehkay/chore/commitlint-esm-config
chore: load commitlint rules by renaming config to .mjs
2026-07-22 19:52:27 +03:00
yusufipk fa1610b053 fix(api): serialize BigInt in all API success responses
successResponse() used NextResponse.json(), which calls JSON.stringify and
throws on BigInt. Prisma returns BigInt for VideoVersion.sizeBytes and
VideoAsset.sizeBytes, so any route returning one of those rows returned 500
after its database write had already committed.

#27 fixed two such endpoints by narrowing their selects, and two create
routes were already wrapped in toJsonSafe(). This closes the bug class at
the helper instead: successResponse() now serializes with a shared
bigIntReplacer, which covers every route in app/api (none construct a
NextResponse.json response directly).

The two toJsonSafe() call sites are now redundant and were removed. BigInt
values render as strings, matching what toJsonSafe already produced.
2026-07-22 23:51:18 +07:00
Yusuf İpek 29d2896cb9 Merge pull request #32 from eehkay/fix/audio-asset-proxy-and-gc-thumbnails
fix: audio asset playback and orphan-cleanup thumbnail deletion
2026-07-22 19:50:28 +03:00
Yusuf İpek 22bb6a68fb Merge pull request #27 from eehkay/fix/bigint-serialization
fix: BigInt serialization 500s in video PATCH and approval decision responses
2026-07-22 19:46:08 +03:00
yusufipk 33008d33ad style: fix prettier formatting 2026-07-12 18:38:17 +07:00
yusufipk 6c6df3cf1d feat: hosted-first landing + accurate fair source licensing copy
- Replace unsubstantiated hero claim with client sign-off messaging
- Rename Open Source (Self-hosted) to Fair Source (Self-hosted) with FSL
  explanation (source visible, self-hostable, Apache 2.0 after two years)
- Put Hosted Cloud first in pricing with Recommended badge and trial note
- Move self-hosting hero link to GitHub, out of primary CTA path
- Add FAQ entries for FSL licensing and 7-day free trial
- Align license language across landing, README, terms, SEO, comparisons
- Fix contact email to [email protected] everywhere (mailto links,
  notification sender fallbacks) and SEO fallback domain
2026-07-12 18:32:38 +07:00
yusufipk 5821f73d38 feat: show live progress while downloading named files
Bunny/cross-origin downloads are fetched into a blob before saving, which
on large files or slow connections looked stuck (spinner only). Stream the
body through a counting transform and show real byte progress in a toast:
per-file percent for single downloads and file N/M + percent for bulk.

- Progress is measured from Content-Length + received bytes (not estimated).
- The blob is assembled by the browser from the stream (can be disk-backed),
  so we don't accumulate chunks in the JS heap.
- Only the blob path shows a toast; same-origin (R2/S3/MinIO) and the >10 GB
  fallback use the browser's native download UI.
2026-07-10 22:27:53 +07:00
yusufipk 8845c2c643 feat: name video downloads by title + version
Downloads now save as "<video title> <version label>" (or "<title> vN"
when no label), with the real extension derived from the file's content
type, instead of the CDN's generic "original" name.

- Bunny (cross-origin CDN redirect) files are fetched and saved as a named
  blob, but only up to 10 GB; larger files fall back to a plain navigation
  so the browser streams to disk without buffering in memory.
- R2 / S3 / MinIO uploads are same-origin (/api/upload/video/...), so the
  download attribute names them correctly at any size, no buffering.
- Applies to both single-video and bulk/project downloads; bulk downloads
  run sequentially so at most one file is buffered at a time.
- Shared helper in lib/client/download-file.ts.
2026-07-10 21:58:09 +07:00
yusufipk bede216081 perf: smooth playhead + live scrubbing preview
Drive the timeline progress fill and playhead directly via a
requestAnimationFrame loop (bypassing React state) so the playhead glides
at the display refresh rate during playback instead of stepping ~4x/sec.

Scrubbing now previews frames live like an editor: while dragging, the
video is seeked with coalescing (one seek in flight, chasing the latest
target) so HLS stays responsive without stale-seek pileup. Playback pauses
during a scrub and resumes on release. Dragging tracks the cursor anywhere
on the page via window listeners.
2026-07-10 21:31:36 +07:00
yusufipk 8d7d064647 feat: make asset downloads opt-in via "Include assets" toggle
Project/selected downloads now include only videos by default. Add an
"Include assets" checkbox toggle to both download dropdowns (default off)
that adds b-rolls and other attached assets to the download when enabled.

- buildProjectDownloadManifest gains an includeAssets option (default false).
- Download route reads ?assets=1 and passes it through.
2026-07-10 21:03:34 +07:00
yusufipk 57c5a127d1 feat: move videos to another project (single + bulk)
Add a "Move to project" action in the video card dropdown and the
selection-mode toolbar. Videos (with their versions, comments, assets and
video-scoped share links) can be moved into another project in the same
workspace.

- New GET/POST /api/projects/[projectId]/videos/move: GET lists manageable
  destination projects in the workspace; POST performs the move.
- Requires canEdit on both source and destination; same-workspace only.
- Move runs in an interactive transaction that re-asserts source ownership
  atomically (updateMany guarded by projectId) to avoid a TOCTOU race, and
  returns 409 on conflict. GET is rate-limited ('api').
2026-07-10 20:55:12 +07:00
yusufipk 654d3a6bc7 style: format download dropdown item (prettier) 2026-07-10 20:08:15 +07:00
yusufipk 34e72f6cbb fix: bulk video download (original quality, latest version by default)
- Accept source=auto in the version download route (was 400 Bad Request),
  so bulk/project downloads of Bunny videos no longer fail.
- Bulk/project downloads now request the original (uncompressed) Bunny file
  so quality never drops (was source=auto which could fall back to compressed).
- Project/selected downloads default to the latest version of each video and
  add a separate "All versions" option in the download dropdowns.
2026-07-10 20:04:18 +07:00
Yusuf İpek cbaecb92cc Merge pull request #24 from yusufipk/fix/comment-newline-rendering
fix: preserve newlines/paragraph breaks in rendered comments
2026-07-10 15:41:29 +03:00
yusufipk d500dcb042 fix: preserve newlines/paragraph breaks in rendered comments
Comment content was stored with newlines intact but rendered inside <p>
elements with default white-space, collapsing line breaks into single
spaces. Add whitespace-pre-wrap (and break-words) to the comment/reply
render wrappers in the comments pane and the compare-versions view.
2026-07-10 19:33:03 +07:00
yusufipk 880d0ac0fa feat: chunked (S3 multipart) uploads for R2/S3 video backend
Self-hosted instances on the R2/S3 backend could only upload a video as a
single PUT, which fails behind a Cloudflare proxy/tunnel (100MB request-body
cap) and is capped at 5GiB with no resilience. Bunny already avoids this via
tus; this brings the R2/S3 path to parity.

Files larger than a threshold (default 90MiB) are now split into parts
(default 32MiB, min 5MiB) and uploaded directly browser->R2 via presigned
UploadPart URLs, then reassembled server-side with CompleteMultipartUpload.
Each request stays under the 100MB cap, lifts the size ceiling well past
5GiB, and adds per-chunk retry. Files at/under the threshold keep the
existing single-PUT path unchanged. Bunny path is untouched.

Thresholds are env-overridable via OPENFRAME_R2_MULTIPART_THRESHOLD_BYTES
and OPENFRAME_R2_MULTIPART_PART_SIZE_BYTES.

Verified end-to-end against real Cloudflare R2 and a local MinIO behind an
nginx 90MB cap (single 141MB PUT 413s on master; 32MB parts pass here).

Closes #22
2026-07-10 19:19:07 +07:00
yusufipk 82932c6b22 fix: scope select-all to current page
"Select all" previously selected every video across every page, which is
too easy to trigger by accident when the user only meant the videos
visible on the current page. Scope select/deselect to the current page's
videos and relabel the button to "Select page"/"Deselect page" when the
project spans multiple pages.
2026-06-27 13:46:05 +02:00
yusufipk cebdf23b38 fix(security): bump nodemailer to 9.0.1
Resolves the high-severity advisory (dependabot #20) where the
message-level raw option bypassed disableFileAccess/disableUrlAccess,
enabling arbitrary file read and SSRF. We only use the standard
createTransport/sendMail API, so the major bump is non-breaking.
2026-06-27 13:38:07 +02:00
yusufipk 95dcf92d8b fix: clamp page after bulk video delete
When every video on the current page was bulk-deleted, router.refresh()
re-queried the same out-of-range page and rendered "No videos yet" even
though earlier pages still had videos. Clamp to the last valid page based
on the remaining video count, falling back to refresh in place.
2026-06-27 13:35:08 +02:00
yusufipk 52e4169db2 feat: add project bulk download and bulk video delete
Add a "Download project" / "Download selected" flow that builds a
server-side manifest of downloadable media, plus a selection mode with
bulk delete for project videos.

Gate viewer downloads behind a new project allowDownloads setting
(default off, opt-in). Admins can always download; enabling on a public
project allows anonymous visitors to download. Enforce the setting on
every download surface (manifest, version, asset, watch, video routes)
via canDownloadProjectMedia.

Add rate limits for the manifest endpoint, host allowlisting for direct
download URLs, and configurable file/byte caps.

Closes #16
Closes #19
2026-06-27 13:24:05 +02:00
yusufipk 9613c4f2c6 fix: harden email validation and CI permissions 2026-06-14 16:59:09 +02:00
yusufipk 56fb7403cf fix: resolve CI lint and formatting failures.
Replace internal anchor tags with Next.js Link components and format the marketing comparison route page.
2026-06-14 16:42:27 +02:00
yusufipk 51257e004f Add SEO comparison landing pages and footer compare links.
Introduces dynamic marketing comparison routes, competitor data, and Compare sections on the homepage and marketing footer.
2026-06-14 16:36:26 +02:00
yusufipk d301f3d808 docs(security): prefer GitHub private vulnerability reporting 2026-06-13 23:28:22 +02:00
Yusuf İpek 0a89e52a98 Merge pull request #21 from yusufipk/cursor/be172284
feat: bulk video uploads and S3 asset video support (fixes #18)
2026-06-14 00:27:09 +03:00
yusufipk 00124bc7c2 feat: bulk video uploads and S3 asset video support (#18)
Add multi-file drag-and-drop queues for project videos and the assets pane, and route asset video uploads through S3/R2 when direct Bunny uploads are disabled.
2026-06-13 23:24:21 +02:00
yusufipk 52ace1a1a8 fix: generate CSP from runtime storage env for self-hosted MinIO
Move Content-Security-Policy generation to proxy.ts so R2_PRESIGN_ENDPOINT
is included at request time instead of being frozen at image build time.
Document reverse-proxy layouts for Docker self-hosting and copy proxy.ts
into the Docker image.

Closes #17
2026-06-12 21:21:31 +02:00
yusufipk 4bf6e821af feat: enable S3 video uploads and update related configurations
- Added support for self-hosted S3 video uploads with new environment variables: OPENFRAME_ENABLE_S3_VIDEO_UPLOADS and OPENFRAME_MAX_VIDEO_UPLOAD_BYTES.
- Updated .env.example and .env.docker.example to reflect new configuration options.
- Enhanced Content Security Policy to include origins for S3-compatible storage.
- Updated dependencies for AWS SDK to support new features.
- Refactored upload logic to accommodate both Bunny and S3 upload providers.
- Updated documentation to clarify the usage of direct uploads and S3 configurations.
- Closes #11
2026-05-27 17:04:39 +02:00
Yusuf İpek b6de3a29aa Merge pull request #15 from yusufipk/dependabot/npm_and_yarn/npm_and_yarn-152f59e559
chore(deps): bump next from 16.2.3 to 16.2.6 in the npm_and_yarn group across 1 directory
2026-05-23 21:46:32 +03:00
yusufipk 6692db992d feat: add YouTube to connect-src in Content Security Policy 2026-05-23 20:44:52 +02:00
yusufipk 378ca1977b feat: enhance comment functionality with timestamp range support
- Added timestampEnd to Comment and CommentReply interfaces.
- Implemented logic for handling comment timestamp ranges in the comment composer and comments pane.
- Updated video player and player core to support frame stepping and improved seeking functionality.
- Introduced frame mode toggle for precise navigation during video playback.
- Closes #12
2026-04-25 22:58:16 +03:00
Yusuf İpek 8329fc73e0 Merge pull request #9 from koksalenes/chore/setup-dx-tools
chore: add husky, commitlint, lint-staged, editorconfig and prettier
2026-04-25 20:31:14 +03:00
Yusuf İpek 066c4247db feat: add sorting functionality to project videos and update search parameters 2026-04-22 15:31:09 +03:00
Yusuf İpek 1014fd503a Merge pull request #7 from poyrazavsever/master
Dökümantasyon: Katkı Süreçlerini Standartlaştırma
2026-04-22 14:31:17 +03:00
Yusuf İpek c73bc9a19a fix(README): correct description of OpenFrame to emphasize fair source 2026-04-20 16:49:18 +03:00
Yusuf İpek a6bbc50acd Merge pull request #4 from cinarozkan/add-ci-pipeline
ci: add GitHub Actions CI pipeline
2026-04-16 18:36:14 +03:00
Yusuf İpek c9522eff82 feat(VideoPageHeader): move DownloadControls to a new position in the layout 2026-04-16 15:27:25 +03:00
Yusuf İpek 6c42fd23d3 feat(LandingPage): add option to download original uploaded video 2026-04-15 19:55:10 +03:00
Yusuf İpek 873945464d feat: implement storage quota management for uploads
- Added storage quota enforcement for audio and image uploads in the respective routes.
- Introduced reservation system to manage concurrent uploads and prevent quota overages.
- Enhanced comment creation to account for audio and image attachment sizes against user quotas.
- Created new UploadReservation model to track in-flight upload reservations.
- Backfilled existing video assets with size information from R2.
- Added progress component for UI feedback during uploads.
- Updated API responses to include reservation IDs for better quota management.
- Adjusted error handling to return appropriate storage limit exceeded messages.
2026-04-15 19:53:43 +03:00
Yusuf İpek acf31b3d6b refactor(docker): remove unnecessary next-env.d.ts copy from Dockerfile 2026-04-15 17:00:41 +03:00
Yusuf İpek 50d31ef894 feat(download): add estimation of egress bytes by fetching Content-Length via HEAD request 2026-04-14 13:56:27 +03:00
Yusuf İpek f3cdebaa81 refactor(download): remove unused content type handling and filename sanitization logic 2026-04-14 13:46:43 +03:00
Yusuf İpek 8a12bb484b feat(assets): add image upload state management and update button behavior during upload 2026-04-14 13:41:41 +03:00
Yusuf İpek d1b2d23509 fix(settings): update workspace creation display logic to show reason only when creation is not allowed 2026-04-11 13:02:30 +03:00
Yusuf İpek e18d5219f5 feat(billing): add trial eligibility logic and update billing overview 2026-04-11 12:53:02 +03:00
Yusuf İpek faa902a604 feat(auth): implement email verification process with resend functionality and update registration flow 2026-04-11 00:17:09 +03:00
Yusuf İpek 5bff32fef1 fix: update descriptions for real-time webhooks and storage exceedance FAQ 2026-04-10 23:04:07 +03:00
Yusuf İpek f622752233 feat(auth): enhance registration page with OAuth support for Google and GitHub 2026-04-10 22:50:02 +03:00
Yusuf İpek c457971ff5 chore: update dependencies to latest versions
- upgraded @aws-sdk/client-s3 from ^3.985.0 to ^3.1029.0
- upgraded next from 16.1.6 to 16.2.3
- upgraded nodemailer from ^8.0.1 to ^8.0.5
2026-04-10 22:41:05 +03:00
Yusuf İpek 20897ab418 feat(privacy, refund, terms): add Privacy Policy, Refund Policy, and Terms of Service pages with navigation links 2026-04-10 22:33:23 +03:00
Yusuf İpek 4ba7521a38 feat(auth): implement OAuth login with Google and GitHub, add Prisma adapter for user management 2026-04-10 22:23:13 +03:00
Yusuf İpek 5beaa26fb4 feat(comments): enhance comment deletion permissions to allow project owners and admins to delete any comment 2026-04-10 21:56:02 +03:00
Yusuf İpek 11abb5dbcb fix(notifications): improve error handling for Telegram test by logging detailed error message 2026-04-10 21:53:56 +03:00
Yusuf İpek 05a303cd35 feat(projects): add validation for name and description in PATCH request to ensure they are non-empty strings within specified length limits 2026-04-10 21:52:46 +03:00
Yusuf İpek db1c594fae feat(progress): add validation for progress and duration to ensure they are non-negative finite numbers within a reasonable range 2026-04-10 21:51:18 +03:00
Yusuf İpek c6f06627f2 feat(progress): add validation for duration to ensure it is a non-negative finite number 2026-04-10 21:50:13 +03:00
Yusuf İpek 79d8b742e3 feat(videos): add validation for position to ensure it is a non-negative integer 2026-04-10 21:49:15 +03:00
Yusuf İpek df10f45e9e feat(onboarding): implement rate limiting for onboarding completion 2026-04-10 21:48:25 +03:00
Yusuf İpek e72f5e8948 feat(projects): add validation for project visibility in PATCH request 2026-04-10 21:47:26 +03:00
Yusuf İpek 9b561b78b4 refactor(request-origin): remove unused origin retrieval functions to enhance security 2026-04-10 21:45:40 +03:00
Yusuf İpek 72bdc05422 feat(comments): add validation for guest email length and format in POST request 2026-04-10 21:43:16 +03:00
Yusuf İpek 1be0e6d6fa feat(tags): add validation to ensure tags belong to the project to prevent IDOR 2026-04-10 21:40:48 +03:00
Yusuf İpek eca3f92e71 feat(env): add trusted proxy mode configuration for rate limiting 2026-04-10 21:39:30 +03:00
Yusuf İpek 288c5d2624 feat(stripe-billing): add Stripe stats retrieval and display in admin dashboard 2026-04-10 21:35:22 +03:00
Yusuf İpek 3d0e430230 feat(access-control): implement project access control for audio and image uploads 2026-04-10 21:27:52 +03:00
Yusuf İpek c92181b90f feat(rate-limit): enforce rate limit configuration in production environment 2026-04-10 21:13:43 +03:00
Yusuf İpek d07b40a937 feat(rate-limit): add rate limit configuration for comment export 2026-04-10 21:12:56 +03:00
Yusuf İpek 84ea384ee7 refactor(invitation): simplify buildInvitationUrl by removing email parameter 2026-04-10 21:11:56 +03:00
Yusuf İpek 8014fc3986 Refactor error logging across the application to use a centralized logger
- Introduced a new logger utility (`logError`) to standardize error logging.
- Replaced all instances of `console.error` with `logError` in various API routes and libraries.
- Enhanced error logging to sanitize sensitive information, particularly for Prisma and Stripe errors.
- Ensured consistent error handling and logging practices throughout the codebase.
2026-04-10 21:10:09 +03:00
Yusuf İpek 07f7b6fb02 feat(security): enhance Content Security Policy and add global security headers 2026-04-10 20:56:33 +03:00
Yusuf İpek 439e74d037 feat(validation): implement validateAnnotationStrokes function for safe annotation data handling
feat(rate-limit): add TRUSTED_PROXY_MODE for configurable proxy header trust
feat(comments): validate annotation data structure in comment routes and components
2026-04-10 20:38:22 +03:00
Yusuf İpek 03bfd565e8 feat(auth): enforce length limits for name and password during registration
feat(comments): add content length validation for comments and annotations

feat(guest-gate): restrict guest name length and update localStorage handling

feat(share-link-unlock): set maxLength for password input field
2026-04-10 20:28:55 +03:00
Yusuf İpek d1ffc4c4db feat(annotation-canvas): optimize canvas resizing by using refs for strokes and currentStroke 2026-04-09 17:23:27 +03:00
Yusuf İpek ea05f9c929 feat(cache): implement max entries limit for Bunny download source cache 2026-04-09 17:18:34 +03:00
Yusuf İpek 26cf58a28c feat(auth): enhance project access handling with pre-fetched data and new utility functions 2026-04-09 17:17:39 +03:00