161 Commits

Author SHA1 Message Date
Dorian
6bb95fd004 fix: Amber login on mobile with two-phase clipboard flow
Mobile browsers block navigator.clipboard.readText() unless called
inside a user gesture (tap/click). The old flow relied on the
visibilitychange event to auto-read the clipboard when the user
returned from Amber, which silently failed.

New flow:
1. User taps "Sign in with Amber" → opens Amber via Android intent
2. User approves in Amber → pubkey copied to clipboard
3. User returns to browser → sees "Complete Sign-in" button
4. User taps "Complete Sign-in" → clipboard read succeeds (user gesture)
5. Pubkey decoded, account registered, backend session created

Also handles npub/nprofile decoding and provides clear error messages
for empty clipboard, missing permissions, and non-Android devices.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 22:23:34 +00:00
Dorian
2fdb119ee5 fix: show backstage films on all content sources, not just IndeeHub API
The filmmakerService.usesSelfHosted() was tied to the content source
toggle, returning true only for 'indeehub-api'. When the user switched
to 'topdocfilms' or 'indeehub', the mergePublishedFilmmakerProjects()
function routed filmmaker API calls to the wrong (external) API, so
backstage-created films never appeared.

Now in production (USE_MOCK=false), filmmaker operations always use
the self-hosted backend regardless of which content catalog is active.
The content source toggle only affects the browse page catalog display.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 22:20:14 +00:00
Dorian
faa419fc28 fix: serve service worker with no-cache headers via exact-match location
The sw.js and workbox-*.js files were being caught by the immutable
static asset regex (expires 1y), causing stale service workers and
potential 502 errors during re-registration. Use location = /sw.js
(exact match, highest Nginx priority) and a regex for workbox files
that appears before the asset cache block.

Also removes the dead duplicate location blocks at the bottom of
the config that were never reached due to regex priority.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 22:16:13 +00:00
Dorian
5db3194d60 fix: suppress unused variable errors after hiding persona/extension UI
vue-tsc flagged variables as unused since their template references are
inside HTML comments. Prefix with underscores and comment out the
related functions/imports so the production build passes cleanly.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 22:05:49 +00:00
Dorian
ad4a9f48b6 ui: hide Persona switcher and Extension button from header
These are dev/testing tools that shouldn't be visible in production.
Commented out for now so they can be re-enabled easily.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 21:58:24 +00:00
Dorian
671afd2915 fix: add GET /subscriptions endpoint to silence 404s
The frontend calls GET /subscriptions but only GET /subscriptions/active
existed. Add a root GET handler that returns the same data.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 21:57:17 +00:00
Dorian
f3a9d3d614 fix: decouple rent payouts from rate API, correct sats terminology
Rent shareholder payouts are denominated in sats and don't need a
USD/sat exchange rate. Skip the getSatoshiRate() call for rent-type
payments so they are never blocked by rate-API 429s or outages.

Also fix a misleading log message that said "USD" instead of "sats".

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 21:34:07 +00:00
Dorian
c84c4e92b7 fix: widen rents.usd_amount column, add webhook alias, silence cron spam
- Migration to ALTER rents.usd_amount from numeric(5,2) to numeric(15,2)
  which was causing "numeric field overflow" 500 errors when saving
  rental prices >= 1000 sats (e.g. 1200 sats)
- Also widen season_rents.usd_amount for consistency
- Add /webhooks/btcpay route alias (BTCPay was posting to /btcpay but
  the endpoint was /btcpay-webhook, causing 404s on payment callbacks)
- Skip ECS autoscaling cron when TRANSCODING_API_URL is not set
  (eliminates "security token invalid" error spam every minute)
- Reduce payment cron from EVERY_MINUTE to EVERY_10_MINUTES to avoid
  429 rate limiting on the BTC price API
- Bump API CACHEBUST to 10

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 21:30:29 +00:00
Dorian
5244fdef50 Fix poster 404: add ^~ to /storage/ locations to override static asset regex
Nginx was serving /storage/.../*.jpg from the local filesystem instead of
proxying to MinIO because the static asset regex location (~* \.(jpg|...)$)
takes priority over plain prefix locations. Adding ^~ ensures the /storage/
and /storage-private/ prefix locations always win over regex matches.

Same root cause as the earlier 405 on thumbnail uploads.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 21:18:11 +00:00
Dorian
7f78ac9ba6 Fix FFmpeg worker: align job data format with API and update content status
The worker was completely broken because of 4 mismatches with the API:

1. Field names: API sends {correlationId, inputKey, outputKey, inputBucket,
   outputBucket} but worker expected {contentId, sourceKey, outputPrefix}.
   All fields were undefined, so jobs silently failed.

2. No status callback: Worker never updated content status to 'completed',
   so projects never appeared as published (content stuck in 'processing').
   Now updates status directly in PostgreSQL.

3. Wrong bucket: Worker uploaded HLS to private bucket, but the stream
   controller checks the public bucket. Now uploads to outputBucket (public).

4. Wrong manifest name: Worker output index.m3u8 but codebase expects
   file.m3u8. Aligned with helper.ts convention.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 21:07:24 +00:00
Dorian
f6c4b9f06c Fix false "Rented" state for project owners + add "Your project" badge
The rental check catch block was incorrectly setting hasActiveRental=true
for project owners when the API call failed (e.g. auth token not synced).
This showed a "Rented" badge with no time remaining and hid the rent button.

- Separate "owner can play" from "has active rental" via new isOwner computed
- canPlay now includes isOwner so owners always have playback access
- Catch block no longer fakes a rental — keeps the badge accurate
- New purple "Your project" badge for owners (distinct from green "Rented")

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 20:48:58 +00:00
Dorian
3e4279e252 Fix 405 on thumbnail uploads: move MinIO proxy above static asset regex
Nginx regex locations are evaluated in order — the static asset caching
rule (.jpg, .png, etc.) was matching image upload URLs before the MinIO
bucket proxy could handle them, causing PUT requests to return 405.

Moved the /indeedhub-*/ proxy location to the top of the regex block.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 20:36:03 +00:00
Dorian
fc20c625fa Fix Mixed Content on file uploads: presigned URLs now use public domain
The backend was generating presigned S3 URLs pointing to the internal
MinIO endpoint (http://minio:9000), which browsers block on HTTPS pages.

- Add a second S3 client in upload.service.ts configured with FRONTEND_URL
  for generating browser-facing presigned URLs (both upload and download)
- Add nginx proxy location for /indeedhub-private/ and /indeedhub-public/
  paths that forwards to MinIO without rewriting (preserves S3v4 signatures)
- Keep internal S3 client for server-side operations (copy, delete, etc.)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 20:30:49 +00:00
Dorian
abb83fe164 Bump CACHEBUST to v8 for backend and frontend rebuilds; update Nginx and NostrAuthGuard to handle X-Forwarded-Prefix for NIP-98 compliance 2026-02-13 20:20:32 +00:00
Dorian
dea2d2e768 Bump CACHEBUST to v7 to force rebuild with payment_methods fix
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 20:08:36 +00:00
Dorian
4f7fdd4413 Fix missing payment_methods table creation + bump CACHEBUST to v6
Migration AddedWithdrawalFrequency1733770884555 referenced payment_methods
table that was never created. Modified to CREATE TABLE IF NOT EXISTS with
all columns, then drop lightning_address from filmmakers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 19:47:46 +00:00
Dorian
34c48d9f6e Fix apostrophe in Children's Animation migration + bump CACHEBUST to v5
Escape single quote in seed-episodic-subgenres migration that caused
"column Children's Animation does not exist" PostgreSQL error.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 19:34:31 +00:00
Dorian
f52e7dda7e Bump CACHEBUST to v4 to force backend rebuild with migration fixes
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 19:13:59 +00:00
Dorian
85e9d2f197 Convert all remaining entity-manager migrations to raw SQL
TypeORM's manager.save/delete with string table names still uses
entity metadata internally (triggers SELECT with all entity columns).
Converted SeedSubgenres, RemoveLastGenres, and SeedEpisodicSubgenres
to use queryRunner.query() with raw SQL to avoid column mismatches.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 19:01:24 +00:00
Dorian
20718c547e Remove image tags that cause Portainer pull failures
Portainer tries to pull named images from Docker Hub before building.
Removing image: tags so it builds directly from Dockerfiles. The
CACHEBUST build arg handles cache invalidation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 18:41:21 +00:00
Dorian
82a5c0a5cf Fix entity-based migrations that crash on missing columns
MusicVideosUpdate and AddEpisodicGenres migrations used TypeORM
entity classes which reference columns that don't exist at their
migration timestamp (e.g. trailer_old, later entity fields).
Rewrote both to use raw SQL INSERT/UPDATE statements.

Also bumped CACHEBUST to v3 to force backend image rebuild.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 18:30:37 +00:00
Dorian
eeffce4baa Add versioned image tags to force Portainer to rebuild images
Docker was reusing old cached images even on redeploy. By adding
explicit image names with version tags (e.g. indeehub-app:v2),
Docker must build new images since the old ones had no tag or a
different tag. Bump the version (v2 -> v3) to force future rebuilds.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 18:11:51 +00:00
Dorian
bb281b488b Add CACHEBUST build arg to force Docker image rebuilds
Docker's build cache was preventing Portainer from picking up
code changes. Adding a CACHEBUST ARG before COPY invalidates
all subsequent layers when the value changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 18:05:40 +00:00
Dorian
330345c1ac Hardcode postgres healthcheck user to avoid variable substitution issues
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 17:54:46 +00:00
Dorian
e9c5c50ca3 Remove MinIO console port mapping to avoid host port conflicts
The MinIO S3 API is accessed internally via the Docker network.
The admin console is not needed externally and was causing
port 9001 conflicts with other services on the host.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 17:50:37 +00:00
Dorian
819b20dcb4 Refactor genre seeding and deletion logic for improved database management
- Simplified the genre seeding process by replacing bulk save with individual insert queries, enhancing performance and error handling.
- Removed unnecessary comments and streamlined the deletion process for genres, ensuring clarity in migration scripts.
- Updated the `down` method to accurately restore deleted genres, maintaining data integrity during rollbacks.
2026-02-13 17:36:53 +00:00
Dorian
8c43448306 Refactor database SSL configuration for improved clarity and flexibility
- Updated SSL settings in database.module.ts and ormconfig.ts to use DATABASE_SSL environment variable for better control over SSL usage.
- Clarified comments to indicate that SSL is only necessary for remote PostgreSQL instances, enhancing understanding for local development setups.
2026-02-13 17:28:28 +00:00
Dorian
b8ab347c68 Update Nginx configuration and mock mode initialization for improved backend handling
- Modified Nginx configuration to trust the outer reverse proxy's X-Forwarded-Proto header, enhancing protocol handling.
- Updated initMockMode function in mock.ts to use the backend health endpoint for improved error handling and timeout management, ensuring a more robust fallback to mock data when the backend is unreachable.
2026-02-13 17:18:44 +00:00
Dorian
c6d5896b30 Update API configuration to support additional environment variables for improved flexibility
- Modified baseURL and cdnURL in api.config.ts to include VITE_INDEEHUB_API_URL, enhancing the ability to switch between different API endpoints.
- Updated initMockMode function in mock.ts to reflect the changes in API URL handling, ensuring consistency across the application.
2026-02-13 17:09:08 +00:00
Dorian
a66842d771 Update environment variables and refactor Docker configurations for improved deployment
- Revised .env.portainer to update sensitive credentials and streamline comments for clarity.
- Adjusted docker-compose.yml to remove unnecessary variable references, enhancing readability and maintainability.
- Updated VideoPlayer component to improve type handling and refactor seeking logic for better performance.
- Enhanced library service to include providerId in the rentContent method for improved data handling.
- Refactored auth store to integrate account management functionality.
- Cleaned up ProjectEditor and Settings views by removing unused computed properties and refining method types.
2026-02-13 16:40:10 +00:00
Dorian
a8dc82dc59 Update environment variables and enhance Docker configurations for improved deployment
- Modified .env.portainer to include new environment variables for S3 private bucket URL, Nostr JWT secrets, and SendGrid options.
- Updated docker-compose.yml to support the new environment variables, enhancing service configurations.
- Added a seed content script to the backend package.json for initializing the database with sample data.
- Refactored helper functions to construct S3 URLs more robustly, accommodating potential missing configurations.
- Enhanced dev.sh script to seed the database if empty, ensuring content availability during development.
2026-02-13 16:27:51 +00:00
Dorian
3ca43b62e4 Enhance Docker and backend configurations for improved deployment
- Updated docker-compose.yml to include environment variable support for services, enhancing flexibility in configuration.
- Refactored Dockerfile to utilize build arguments for VITE environment variables, allowing for better customization during builds.
- Improved Nginx configuration to handle larger video uploads by increasing client_max_body_size to 5GB.
- Enhanced backend Dockerfile to include wget for health checks and improved startup logging for database migrations.
- Added validation for critical environment variables in the backend to ensure necessary configurations are present before application startup.
- Updated content streaming logic to support direct HLS URL construction, improving streaming reliability and user experience.
- Refactored various components and services to streamline access checks and improve error handling during content playback.
2026-02-13 12:35:03 +00:00
Dorian
7e9a35a963 Add HLS.js support and enhance content streaming logic
- Integrated HLS.js version 1.6.15 into the project for improved video streaming capabilities.
- Updated the ContentsController to check for HLS manifest availability and fall back to presigned URLs for original files if not found.
- Enhanced the VideoPlayer component to handle loading and error states more effectively, improving user experience during streaming.
- Refactored content service methods to return detailed streaming information, including HLS and DASH manifest URLs.
2026-02-13 00:04:53 +00:00
Dorian
0da83f461c Enhance payment processing and rental features
- Updated the BTCPay service to support internal Lightning invoices with private route hints, improving payment routing for users with private channels.
- Added reconciliation methods for pending rents and subscriptions to ensure missed payments are processed on startup.
- Enhanced the rental and subscription services to handle payments in satoshis, aligning with Lightning Network standards.
- Improved the rental modal and content detail components to display rental status and pricing more clearly, including a countdown for rental expiration.
- Refactored various components to streamline user experience and ensure accurate rental access checks.
2026-02-12 23:24:25 +00:00
Dorian
cdd24a5def Implement backend API and database services in Docker setup
- Added a new `api` service for the NestJS backend, including health checks and dependencies on PostgreSQL, Redis, and MinIO.
- Introduced PostgreSQL and Redis services with health checks and configurations for data persistence.
- Added MinIO for S3-compatible object storage and a one-shot service to initialize required buckets.
- Updated the Nginx configuration to proxy requests to the new backend API and MinIO storage.
- Enhanced the Dockerfile to support the new API environment variables and configurations.
- Updated the `package.json` and `package-lock.json` to include new dependencies for QR code generation and other utilities.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 20:14:39 +00:00
Dorian
f19fd6feef Enhance comment seeding and search functionality
- Updated the `seedComments` function to return an array of published comment event IDs for tracking.
- Introduced `seedCommentReactions` to seed upvotes and downvotes on comments, improving interaction visibility.
- Enhanced the `App.vue` and `MobileNav.vue` components to support a mobile search overlay, allowing users to search films seamlessly.
- Added a new `MobileSearch` component for better search experience on mobile devices.
- Implemented a search feature in `AppHeader.vue` with dropdown results for improved content discovery.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 14:57:16 +00:00
Dorian
53a88b012a Update tsconfig and AuthModal component for enhanced functionality
- Added new composable `usecontentdiscovery` and `contentsource` to support additional content sources in the application.
- Removed unused `isAmberSupported` from the AuthModal component to streamline the authentication process.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 14:26:52 +00:00
Dorian
35bc78b890 Enhance content management and user interaction features
- Introduced a new content source toggle in the profile and app header to switch between IndeeHub and TopDoc films.
- Updated the content fetching logic to dynamically load content based on the selected source.
- Enhanced the seeding process to include a combined catalog of IndeeHub and TopDoc films, ensuring diverse content availability.
- Improved user interaction by preventing duplicate reactions and ensuring a smoother voting experience across comments and content.
- Added support for Amber login (NIP-55) for Android users, integrating it into the existing authentication flow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 14:24:52 +00:00
Dorian
ab0560de00 Add WebSocket support and update dependencies
- Introduced WebSocket polyfill for Node.js in seed-activity.ts and seed-profiles.ts to support applesauce-relay and RxJS.
- Updated package.json and package-lock.json to include 'ws' version 8.19.0 and '@types/ws' version 8.18.1 as dependencies.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 13:25:58 +00:00
Dorian
4bc14caae7 Match algo filter layout to Films/My List (slider spacing)
Replaced the grid layout on the algo filter view with the same flex
slider container (gap-8, pt-6 pb-8, fixed card widths) used by
ContentRow and My List, with flex-wrap so all items remain visible.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 13:21:13 +00:00
Dorian
dcb419d67a Unify auth: bridge auth store and Nostr account on every login path
The app had two disconnected auth systems:
- Auth store (useAuth): controls isAuthenticated, subscription, My List
- Account manager (useAccounts): controls isNostrLoggedIn, comments

Previously each login path only populated one system:
- Persona login → Nostr only (no subscription/My List)
- AuthModal Nostr → Auth store only (no commenting)
- Extension login → Nostr only (no subscription/My List)

Now every login path bridges both systems:
- Persona/extension login also calls auth store loginWithNostr
- AuthModal Nostr login also registers extension in accountManager
- Logout already cleared both (no change needed)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 13:20:35 +00:00
Dorian
32e1751df3 Fix seeder: origin mismatch + robust Dockerfile
Two root causes for seeding not working on production:

1. Origin mismatch: The seeder writes content IDs as
   http://localhost:7777/content/... but the app was using
   window.location.origin (the user's actual browser URL) to
   query the relay. Introduced VITE_CONTENT_ORIGIN env var
   baked into the Docker build so both sides use the same origin.

2. Dockerfile.seed fragility: Replaced --omit=dev + global tsx
   with a cleaner approach that strips sharp from package.json
   (the only native dep that fails on Alpine) then does a full
   npm install, ensuring tsx/esbuild and all applesauce deps
   resolve correctly.

Also improved wait-for-relay to accept any HTTP response (some
relays return 4xx for plain GET) and increased max attempts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 13:17:49 +00:00
Dorian
57db25aa94 Match My List layout to Films tab (horizontal slider, card sizing)
Replaced grid layout in My List sections with the same horizontal
scroll slider used by ContentRow: flex gap-8, fixed card widths
(200px/280px), matching padding and overflow behavior. Cards,
spacing, and scrolling now look identical across Films and My List.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 13:12:22 +00:00
Dorian
5e6c1e009a Match My List and filter grid text styling to ContentRow
Title and description text sizes in the My List (Continue Watching,
Saved Films, Rentals) and filtered grid views were smaller than
the Films tab ContentRow cards. Aligned all to use the same
text-base/md:text-xl title and text-base description sizing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 13:06:23 +00:00
Dorian
4fb16e079e Enable mock data mode in Docker build
Without a backend API server in the Docker deployment, the app was
trying to reach localhost:4000 causing a 30s timeout on page load
(content store) and a connection error on Nostr login (auth store).

Setting VITE_USE_MOCK_DATA=true makes both use the built-in mock
data and local Nostr relay instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 13:02:11 +00:00
Dorian
e3ce88dc13 Fix seeder: install tsx globally, add verbose CMD output
- Install tsx globally to avoid lockfile conflicts with --omit=dev
- Use set -ex in CMD so Docker logs show exactly which step fails
- Production deps installed cleanly via npm ci --omit=dev

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 12:58:49 +00:00
Dorian
dd4d5e99a4 Fix seeder: skip heavy devDeps, decouple app from seeder success
- Use npm ci --omit=dev to avoid building sharp (needs native libvips
  not available on Alpine) then install tsx separately
- Change app depends_on from seeder (service_completed_successfully)
  to relay only, so the app still starts even if seeding fails

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 12:56:31 +00:00
Dorian
33190afd08 Fix seeder Dockerfile: remove --ignore-scripts from npm ci
The --ignore-scripts flag prevented esbuild (used by tsx) from
downloading its platform binary, causing the seed scripts to
fail with exit 1 in the Docker container.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 12:52:12 +00:00
Dorian
42bd54783a Remove container_name from docker-compose to fix redeploy conflicts
Portainer fails to redeploy when the old containers still exist
with the same hardcoded names. Letting Compose auto-name them
based on the stack/project avoids the conflict.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 12:50:02 +00:00
Dorian
2c6e311705 Add truncated descriptions to My List film cards
Add the same truncated description line to Continue Watching
and My Rentals grids so card sizing matches the other screens.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 12:47:33 +00:00