Production deploy: stream API field mapping, Caddy domain config, systemd service, Docker bridge binding

This commit is contained in:
BizzleBot 2026-02-19 17:36:51 +00:00
parent c6b8fb752a
commit 8f4e5dea40
11 changed files with 46 additions and 22 deletions

View File

@ -43,6 +43,15 @@ export default async function DonatePage() {
</p>
</header>
{/* Coming soon notice */}
<div className="bg-amber-500/10 border border-amber-500/30 rounded-2xl p-6 flex items-center gap-4">
<span className="text-2xl">🚧</span>
<div>
<h3 className="font-bold text-amber-400 text-lg">Online Donations Coming Soon</h3>
<p className="text-stone-400 text-sm">We&apos;re setting up secure payment processing. In the meantime, please visit <a href="https://ccfriendsofwildlife.org" className="text-teal underline" target="_blank" rel="noopener">ccfriendsofwildlife.org</a> to donate directly.</p>
</div>
</div>
{/* Impact banner */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-px bg-white/5 rounded-2xl overflow-hidden">
{[

View File

@ -0,0 +1,5 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default prisma;

View File

@ -1,12 +1,11 @@
import { Router, Request, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { hashPassword, verifyPassword } from '../utils/password';
import { signToken } from '../utils/jwt';
import { authenticate, AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
const validateRegister = [
body('email').isEmail().normalizeEmail(),

View File

@ -1,10 +1,9 @@
import { Router, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// GET /api/burrows — public
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {

View File

@ -1,10 +1,9 @@
import { Router, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { optionalAuth, authenticate, requireRole, AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// POST /api/donations — public (guest) or authenticated
// Stripe integration stub: stores intent, returns client_secret placeholder

View File

@ -1,10 +1,9 @@
import { Router, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// GET /api/events — public
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {

View File

@ -1,10 +1,9 @@
import { Router, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// GET /api/sightings — public
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {

View File

@ -1,9 +1,8 @@
import { Router, Response, NextFunction } from 'express';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// GET /api/stats — public dashboard stats
router.get('/', async (_req: AuthRequest, res: Response, next: NextFunction) => {

View File

@ -1,16 +1,28 @@
import { Router, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { authenticate, requireRole, AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// GET /api/streams — public
function mapStream(s: any) {
return {
id: s.id,
name: s.name,
location: s.camera_location,
status: s.status,
viewerCount: 0,
hlsUrl: s.stream_url,
thumbnailUrl: s.thumbnail_url,
createdAt: s.created_at,
};
}
router.get('/', async (_req: AuthRequest, res: Response, next: NextFunction) => {
try {
const streams = await prisma.livestreamSource.findMany({ orderBy: { status: 'asc' } });
res.json(streams);
res.json(streams.map(mapStream));
} catch (err) { next(err); }
});
@ -19,7 +31,7 @@ router.get('/:id', async (req: AuthRequest, res: Response, next: NextFunction) =
try {
const stream = await prisma.livestreamSource.findUnique({ where: { id: req.params.id } });
if (!stream) { res.status(404).json({ error: 'Stream not found' }); return; }
res.json(stream);
res.json(mapStream(stream));
} catch (err) { next(err); }
});

View File

@ -1,10 +1,9 @@
import { Router, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
import { PrismaClient } from '@prisma/client';
import prisma from '../lib/prisma';
import { authenticate, requireRole, AuthRequest } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// ─── VOLUNTEER HOURS ─────────────────────────────────────────────────────────

View File

@ -8,7 +8,7 @@ services:
environment:
POSTGRES_DB: owl_stream
POSTGRES_USER: owl_user
POSTGRES_PASSWORD: ${DB_PASSWORD:-owl_secure_password}
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD must be set in .env}
volumes:
- owl_pg_data:/var/lib/postgresql/data
networks:
@ -23,6 +23,10 @@ services:
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
memory: 256M
owl-backend:
build:
@ -34,11 +38,11 @@ services:
owl-db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://owl_user:${DB_PASSWORD:-owl_secure_password}@owl-db:5432/owl_stream
JWT_SECRET: ${JWT_SECRET:-owl_jwt_secret_change_me}
DATABASE_URL: postgresql://owl_user:${DB_PASSWORD:?DB_PASSWORD required}@owl-db:5432/owl_stream
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET required}
PORT: 3020
HOST: 0.0.0.0
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-http://localhost:8089}
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-https://owls.bizzle.cloud,http://localhost:8089}
ports:
- "127.0.0.1:3020:3020"
networks:
@ -71,6 +75,7 @@ services:
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://127.0.0.1:3020}
ports:
- "127.0.0.1:8110:8089"
- "172.18.0.1:8110:8089"
networks:
- owl-network
healthcheck: