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>
This commit is contained in:
Dorian
2026-02-12 20:14:39 +00:00
parent f19fd6feef
commit cdd24a5def
478 changed files with 55355 additions and 529 deletions

View File

@@ -0,0 +1,60 @@
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import * as jwt from 'jsonwebtoken';
import { SubscriptionType } from '../enums/types.enum';
@Injectable()
export class PayWithFlashAuthGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const authHeader: string | undefined = request.headers.authorization;
const token = (authHeader ?? '').split(' ')[1];
const { url } = request;
// get the type from the URL
const type: SubscriptionType = url.split('/').pop() as SubscriptionType;
if (!token) {
throw new UnauthorizedException('No token provided');
}
let secret: string;
switch (type) {
case 'rss-addon': {
secret = process.env.FLASH_JWT_SECRET_RSS_ADDON;
break;
}
case 'verification-addon': {
secret = process.env.FLASH_JWT_SECRET_VERIFICATION_ADDON;
break;
}
case 'enthusiast': {
secret = process.env.FLASH_JWT_SECRET_ENTHUSIAST;
break;
}
case 'film-buff': {
secret = process.env.FLASH_JWT_SECRET_FILM_BUFF;
break;
}
case 'cinephile': {
secret = process.env.FLASH_JWT_SECRET_CINEPHILE;
break;
}
default: {
throw new UnauthorizedException('Invalid subscription type');
}
}
try {
const decoded = jwt.verify(token, secret);
request.user = decoded; // Attach user information to the request
return true;
} catch {
throw new UnauthorizedException('Invalid token');
}
}
}

View File

@@ -0,0 +1,41 @@
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Subscriptions } from '../decorators/subscriptions.decorator';
import { User } from 'src/users/entities/user.entity';
import { Subscription } from '../entities/subscription.entity';
@Injectable()
export class SubscriptionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const subscriptions = this.reflector.get(
Subscriptions,
context.getHandler(),
);
if (!subscriptions) {
return true;
}
const enabledSubscriptions = new Set(subscriptions);
const request = context.switchToHttp().getRequest();
const user: User = request.user;
if (subscriptions.includes('filmmaker')) return !!user.filmmaker;
if (this.hasActiveSubscription(user.subscriptions)) {
return user.subscriptions.some((sub) =>
enabledSubscriptions.has(sub.type),
);
}
return true;
}
hasActiveSubscription(subscriptions: Subscription[]) {
if (subscriptions.length === 0) return false;
const latestSubscription = subscriptions[0];
const currentDate = new Date();
// get subscription expiration date
const subscriptionExpirationDate = latestSubscription.periodEnd;
// check if subscription is active
return currentDate < subscriptionExpirationDate;
}
}