Commit Graph
41 Commits
Author SHA1 Message Date
yusufipek fe1faeced4 fix(billing): pin the Stripe API version and stop unpaid periods granting access
The Stripe client was built without an apiVersion, so the SDK followed whatever
version it shipped with. Two fields moved in the Basil API version: the billing
period went from the subscription onto its items, and the invoice link to its
subscription went under parent.subscription_details. Both reads returned
undefined without failing, which left stripeCurrentPeriodEnd null for every
subscriber and left the app with no invoice handling at all. A customer whose
card failed saw nothing about the invoice that was still retrying, and a
cancellation did nothing to stop those retries.

- Pin the API version, with `satisfies` so an SDK bump is a compile error here
  before it is a null read in production.
- Read the period off subscription items and the subscription off invoice
  parents, keeping the legacy fields as a fallback for older payloads.
- Handle invoice.paid, invoice.payment_failed, invoice.voided and
  invoice.marked_uncollectible through the existing customer-wide resync, so
  the mirror reflects payment health during dunning rather than after it.
- Add an in-app cancellation route: at period end when the subscription is
  paid, immediately plus voiding the open invoices when it is not, because
  cancelling alone does not stop collection on an invoice already issued.
- Ask Stripe, not just the local mirror, before opening checkout.
- Show the open invoice, the retry date and a payment-method-update shortcut in
  settings, and put a confirmation in front of cancellation.

Access no longer rests on the reported period alone. Stripe advances the period
when it issues the renewal invoice, paid or not, and the period survives
cancellation, so once the period field started being read correctly that check
would have handed a full free month to anyone whose renewal failed, and the new
cancel route would have let them void the invoice and keep the month. Access now
follows the subscription status, billingAccessEndedAt is enforced as a hard
cutoff in both hasBillingAccess and the query that mirrors it, and a subscription
behind on payment keeps access for Stripe's retry window rather than for the
period it never paid for.
2026-09-08 13:30:42 +03:00
yusufipek 6f575e48bf chore(release): set the package version to 0.1.1
The tag and the manifest had drifted: v0.1.1 ships the runtime Bunny CDN
config, while package.json still read 0.1.0. bun.lock records no version for
the root workspace, so a frozen install is unaffected.
2026-08-20 17:11:16 +03:00
yusufipk b51e690062 fix: close the findings the test suite surfaced
The suite that landed in #43/#44 was written against existing behaviour, so a
number of tests pinned bugs rather than asserting correct behaviour. This fixes
the production code and moves each of those tests onto the fixed behaviour in
the same change.

Security:

- project-download: derive the archive entry extension from the last path
  segment and restrict it to a short alphanumeric run, so an extensionless
  allowlisted url can no longer contribute a path separator; validate the r2
  branch against the strict proxy-path pattern instead of a `startsWith`, which
  let `/api/upload/video/clip.mp4/../../etc/passwd` through verbatim.
- rate-limit: hash a key or action wider than its column instead of skipping the
  query. Both the guard and the failing INSERT used to answer "allowed", so the
  limit stopped applying entirely. Warn at startup when TRUSTED_PROXY_MODE is
  unset in production.
- video uploads: the file name decides the content type; a client-declared video
  mime no longer makes `payload.exe` acceptable.
- email templates: escape in the helpers rather than relying on every caller,
  with an explicit `rawEmailHtml()` opt-out for the one call site that builds
  markup. `escapeHtml` now covers the single quote.
- CSP: allow loopback object storage outside production only.
- route-access: reach the billing redirect only for the workspace owner. Keying
  it off the owner's billing status alone made the redirect target an oracle for
  whose subscription had lapsed, and sent members to a page they cannot act on.
- search: carry the same billing condition every other read path carries.
- logger: check `err.name` as well as `err.constructor.name`, so a re-thrown,
  deserialised or minified Prisma error is still redacted.
- upload tokens: resolve the signing secret outside the try, so a server booted
  without one fails loudly instead of reporting every grant as a forgery.
- invitations: never downgrade an existing membership, and report a scoped
  invitation that points at nothing as not_found rather than accepted.
- auth: resolve the workspace role for every signed-in caller, so
  checkProjectAccess and computeProjectAccess stop disagreeing about the owner
  who also owns the workspace. The `intent` option is gone with it.
- r2-media-proxy: validate the object key inside the proxy so the guard travels
  with the function; delete the unused, unanchored `mediaUrlToR2Key`.
- r2: sign the content type into presigned PUT grants.

Correctness:

- frame rate snapping picks the nearest standard, not the first within
  tolerance, so 24, 30 and 60 fps are reachable at all.
- a version upload registers its Bunny cleanup as soon as bunny-init answers, so
  a failed tus upload no longer leaves a billed video behind.
- deleting videos clears storage before the rows, so a refused DELETE leaves a
  retryable row rather than an orphaned object.
- an expired upload session can be cancelled, which is what releases its quota.
- `voice/` joins the delete allowlist, so a voice note can be removed by the
  module that wrote it.
- a failed CORS write propagates instead of being mistaken for an empty config
  and replacing the bucket's rules.
- filtering projects by workspace no longer hides projects the unfiltered call
  returns.
- upload retries skip aborts and permanent 4xx; progress no longer divides by
  zero.
- reply edits no longer clear the comment's tag; optimistic resolve rolls back
  to the state it replaced; the delete snapshot is captured once.
- assorted UI fixes: duplicate React keys, double-click guards reading stale
  closures, the tag list fetched twice per load, a failed member list rendering
  as an empty one, a stale "Initializing upload..." beside a failure, and a
  registration banner pointing at an email that never arrives.

Consistency and access:

- the two download routes answer 404 for an id belonging to another tenant, as
  the comment export route already did. A caller who does belong still gets 403.
- accessible names for the share-link password field, the guest name gates, the
  version dialog inputs and the comment-tag controls.

Repository health:

- the runner image installs production dependencies only.
- a setup file for the unit project restores stubbed env centrally.
- native tsconfig path resolution replaces vite-tsconfig-paths.
- `uploadBytesWithProgress` exists once.
- admin stats bill Bunny storage to the workspace owner like every other
  quota, gate on the configured flag, wire up the single-flight guard and count
  the statuses that belonged to no bucket.
- `r2Client.destroy()` releases the presign client too.
- `prepare` tolerates a production install, where husky is absent.
2026-07-26 18:53:54 +07:00
yusufipk e3fcbf30bf fix(build): move the test db bootstrap under tests/
`.dockerignore` excludes `tests`, so the production build context carries
scripts/test-db-bootstrap.ts without the tests/setup/db-global module it
imports. tsconfig includes `**/*.ts`, so the `prebuild` typecheck fails on the
missing module and every deploy since the test suites landed has died there.
CI never saw it because tests/ exists on a runner.

The script is test-only, so it belongs in the tree that is already ignored.
2026-07-26 15:48:55 +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 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
dependabot[bot] 5ad01d620d chore(deps): bump next in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [next](https://github.com/vercel/next.js).


Updates `next` from 16.2.6 to 16.2.11
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.2.6...v16.2.11)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.2.11
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-07-25 10:20:37 +00: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
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 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 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
dependabot[bot] 1dcbb78b22 chore(deps): bump next in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [next](https://github.com/vercel/next.js).


Updates `next` from 16.2.3 to 16.2.6
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v16.2.3...v16.2.6)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.2.6
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-05-12 23:06:01 +00:00
Enes Köksal 3cfea40fbd refactor: eslint and prettier conflict will be resolved and formatted 2026-04-23 17:05:43 +03:00
Enes Köksal 385b61f29b chore: add husky, commitlint, lint-staged, editorconfig and prettier 2026-04-17 22:54:05 +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 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 e97d60cf36 feat(docker): add Docker support with configuration files and entrypoint scripts 2026-04-09 15:54:03 +03:00
Yusuf İpek 6f22b0bf8b feat(billing): integrate Stripe for subscription management and billing access
- Added billing-related fields to the User model in the database.
- Implemented functions for managing billing access, including trial periods and subscription statuses.
- Created new billing utility functions for Stripe integration.
- Updated onboarding page to include billing overview and workspace creation eligibility.
- Enhanced route access checks to require billing access for certain actions.
- Implemented cleanup scripts for expired billing workspaces and associated media.
- Updated header component to conditionally show app navigation based on billing access.
- Added new migrations for billing-related database changes.
2026-04-08 17:50:40 +03:00
Yusuf İpek 53f7c326df feat: add sharp dependency to enhance image processing capabilities 2026-02-26 15:34:40 +03:00
Yusuf İpek 3522c3da30 feat(video-page): enhance Bunny asset handling and playback features
- Added support for tracking Bunny asset readiness and processing states.
- Implemented thumbnail loading error handling and retry logic for Bunny assets.
- Introduced a "Ready to play" indicator for Bunny assets.
- Enhanced Bunny preview player with playback speed and quality selection options.
- Improved state management for video playback, including resuming playback after source switches.
- Updated package.json to streamline database setup commands.
2026-02-26 11:48:21 +03:00
Yusuf İpek bb43a07234 refactor(db): move rate-limit extras into Prisma migration and remove db-extras script 2026-02-26 10:40:36 +03:00
Yusuf İpek fa51847aa4 feat(storage): add shared R2 media proxy and orphan cleanup tooling for R2/Bunny 2026-02-24 17:02:54 +03:00
Yusuf İpek d15e5192ac chore: clean up lint/type issues and tighten comment media URL validation 2026-02-23 16:06:43 +03:00
Yusuf İpek f224b0a8a1 feat(landing): redesign homepage with GSAP-driven landing page and new showcase assets 2026-02-22 15:53:53 +03:00
Yusuf İpek 47f12b38fa chore: remove unused dependencies and delete unused combobox demo components 2026-02-22 13:42:52 +03:00
Yusuf İpek e30b4a5b19 feat: harden Bunny upload flow, migrate Bunny playback to hls.js, and add Bunny storage admin stats 2026-02-22 13:03:45 +03:00
Yusuf İpek 0f24bcfe6c feat: Implement direct video file uploads via Bunny.net and TUS protocol, adding a new API route and UI for file selection. 2026-02-22 09:55:08 +03:00
Yusuf İpek a8711db0ad build(deps): add radix-ui dependency 2026-02-14 17:26:18 +03:00
Yusuf İpek ea21d173b2 perf(config): improve performance with image optimization, caching headers, and build optimizations
- Add AVIF and WebP image formats for optimized delivery
- Configure cache headers for static assets (images, icons, SVGs)
- Enable server minification and optimize package imports
- Disable poweredByHeader and enable compression
- Remove unused radix-ui dependency
2026-02-14 17:15:35 +03:00
Yusuf İpek 1c7ee70815 feat(optimizations): adds memoization for performance optimization
Add currentUserName to video API responses and session for display in comments. Also adds memoization for performance optimization and implements Page Visibility API to pause comment polling when tab is hidden.
2026-02-14 16:21:52 +03:00
Yusuf İpek d42db4484b feat: add radix-ui dependency 2026-02-10 14:11:40 +03:00
Yusuf İpek b22fd1476f feat: optimize bundle size and improve image loading
- Unify video page components into single VideoPageContent component
  - Reduces ~2300 lines of duplicated code between dashboard and watch pages
  - Uses mode prop ('dashboard' | 'watch') to handle differences

- Move shadcn to devDependencies (CLI tool, not needed at runtime)

- Remove radix-ui meta-package (components already imported individually)

- Replace <img> tags with next/image for automatic optimization
  - Add remotePatterns config for YouTube, Vimeo, Unsplash domains
  - Update video-card.tsx, videos/new/page.tsx, component-example.tsx

- Add next/dynamic for KeyboardShortcutsModal (lazy load on demand)
2026-02-10 14:08:52 +03:00
Yusuf İpek 49c3ac61ea refactor: Add explicit types for workspace and project member checks 2026-02-07 17:28:46 +03:00
Yusuf İpek d01c2247b5 refactor: Simplify project type mapping in WorkspacePage 2026-02-07 17:21:44 +03:00
Yusuf İpek eef03a970c refactor: Refine workspace project types
- Explicitly type `project` in `WorkspacePage`
- Add `typecheck` script to `package.json`
- Improve type safety and development experience
2026-02-07 17:13:32 +03:00
Yusuf İpek 669f6fa9d2 feat: Add optimistic UI for video comments
- Implement optimistic updates for comment creation, deletion, resolution, tag, and content changes
- Enhance user experience by providing immediate feedback for comment actions
- Integrate `sonner` toasts for success and error notifications
- Improve video data fetching error handling and logging
- Refine active video version selection logic for robustness
- Simplify video detail not-found page, removing dynamic project link
2026-02-07 16:21:15 +03:00
Yusuf İpek 88dcf9514c feat: Introduce guest access for video viewing, implement user notification settings via email and Telegram, and add rate limiting infrastructure. 2026-02-07 13:56:26 +03:00
Yusuf İpek 296c5257a7 feat: implement R2 audio file management and rate limiting enhancements
- Add R2 client setup and audio upload functionality in lib/r2.ts.
- Create audio file cleanup functions in lib/r2-cleanup.ts to delete voice files associated with videos, projects, and workspaces.
- Enhance rate limiting in lib/rate-limit.ts with new action-specific limits and improved IP validation.
- Introduce a unified rate limit check function that returns a 429 response when limits are exceeded.
- Update package.json to include the AWS SDK for S3.
2026-02-07 12:27:40 +03:00
Yusuf İpek 5b436fff2d feat: Implement secure email/password authentication with user registration, API rate limiting, and dynamic homepage navigation. 2026-02-07 06:43:09 +03:00
Yusuf İpek d38e8b8749 feat: Implement core API routes for managing projects, videos, versions, and comments, including database seeding. 2026-02-07 06:18:30 +03:00
Yusuf İpek 264392c2ec chore(init): scaffold OpenFrame — Next.js + Bun + shadcn
Initialize OpenFrame project with core scaffold and UI foundation.

- Project scaffold: Next.js 16.1 (App Router) + Bun runtime
- UI: shadcn/ui + TailwindCSS; landing, dashboard, project, and auth UIs
- Video support: provider abstraction (YouTube first), video player page with timestamped comments and custom timeline
- Auth & DB: NextAuth skeleton and Prisma schema (Postgres) included
- UX: dark-mode toggle, full-width layouts, comments sidebar, video route moved to /watch/[videoId]
- Dev: added YouTube iframe integration, custom controls, and TypeScript types
- Next steps: DB migrations, API routes (CRUD), real data wiring, voice-recording & sharing
2026-02-05 22:11:20 +03:00