Production deploy: stream API field mapping, Caddy domain config, systemd service, Docker bridge binding
This commit is contained in:
parent
c6b8fb752a
commit
8f4e5dea40
@ -43,6 +43,15 @@ export default async function DonatePage() {
|
|||||||
</p>
|
</p>
|
||||||
</header>
|
</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'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 */}
|
{/* Impact banner */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-px bg-white/5 rounded-2xl overflow-hidden">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-px bg-white/5 rounded-2xl overflow-hidden">
|
||||||
{[
|
{[
|
||||||
|
|||||||
5
backend/src/lib/prisma.ts
Normal file
5
backend/src/lib/prisma.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export default prisma;
|
||||||
@ -1,12 +1,11 @@
|
|||||||
import { Router, Request, Response, NextFunction } from 'express';
|
import { Router, Request, Response, NextFunction } from 'express';
|
||||||
import { body, validationResult } from 'express-validator';
|
import { body, validationResult } from 'express-validator';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { hashPassword, verifyPassword } from '../utils/password';
|
import { hashPassword, verifyPassword } from '../utils/password';
|
||||||
import { signToken } from '../utils/jwt';
|
import { signToken } from '../utils/jwt';
|
||||||
import { authenticate, AuthRequest } from '../middleware/auth';
|
import { authenticate, AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
const validateRegister = [
|
const validateRegister = [
|
||||||
body('email').isEmail().normalizeEmail(),
|
body('email').isEmail().normalizeEmail(),
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { Router, Response, NextFunction } from 'express';
|
import { Router, Response, NextFunction } from 'express';
|
||||||
import { body, validationResult } from 'express-validator';
|
import { body, validationResult } from 'express-validator';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
|
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// GET /api/burrows — public
|
// GET /api/burrows — public
|
||||||
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { Router, Response, NextFunction } from 'express';
|
import { Router, Response, NextFunction } from 'express';
|
||||||
import { body, validationResult } from 'express-validator';
|
import { body, validationResult } from 'express-validator';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { optionalAuth, authenticate, requireRole, AuthRequest } from '../middleware/auth';
|
import { optionalAuth, authenticate, requireRole, AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// POST /api/donations — public (guest) or authenticated
|
// POST /api/donations — public (guest) or authenticated
|
||||||
// Stripe integration stub: stores intent, returns client_secret placeholder
|
// Stripe integration stub: stores intent, returns client_secret placeholder
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { Router, Response, NextFunction } from 'express';
|
import { Router, Response, NextFunction } from 'express';
|
||||||
import { body, validationResult } from 'express-validator';
|
import { body, validationResult } from 'express-validator';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
|
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// GET /api/events — public
|
// GET /api/events — public
|
||||||
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { Router, Response, NextFunction } from 'express';
|
import { Router, Response, NextFunction } from 'express';
|
||||||
import { body, validationResult } from 'express-validator';
|
import { body, validationResult } from 'express-validator';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
|
import { authenticate, requireRole, optionalAuth, AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// GET /api/sightings — public
|
// GET /api/sightings — public
|
||||||
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
router.get('/', optionalAuth, async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { Router, Response, NextFunction } from 'express';
|
import { Router, Response, NextFunction } from 'express';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { AuthRequest } from '../middleware/auth';
|
import { AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// GET /api/stats — public dashboard stats
|
// GET /api/stats — public dashboard stats
|
||||||
router.get('/', async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
router.get('/', async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
|
|||||||
@ -1,16 +1,28 @@
|
|||||||
import { Router, Response, NextFunction } from 'express';
|
import { Router, Response, NextFunction } from 'express';
|
||||||
import { body, validationResult } from 'express-validator';
|
import { body, validationResult } from 'express-validator';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { authenticate, requireRole, AuthRequest } from '../middleware/auth';
|
import { authenticate, requireRole, AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// GET /api/streams — public
|
// 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) => {
|
router.get('/', async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
||||||
try {
|
try {
|
||||||
const streams = await prisma.livestreamSource.findMany({ orderBy: { status: 'asc' } });
|
const streams = await prisma.livestreamSource.findMany({ orderBy: { status: 'asc' } });
|
||||||
res.json(streams);
|
res.json(streams.map(mapStream));
|
||||||
} catch (err) { next(err); }
|
} catch (err) { next(err); }
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -19,7 +31,7 @@ router.get('/:id', async (req: AuthRequest, res: Response, next: NextFunction) =
|
|||||||
try {
|
try {
|
||||||
const stream = await prisma.livestreamSource.findUnique({ where: { id: req.params.id } });
|
const stream = await prisma.livestreamSource.findUnique({ where: { id: req.params.id } });
|
||||||
if (!stream) { res.status(404).json({ error: 'Stream not found' }); return; }
|
if (!stream) { res.status(404).json({ error: 'Stream not found' }); return; }
|
||||||
res.json(stream);
|
res.json(mapStream(stream));
|
||||||
} catch (err) { next(err); }
|
} catch (err) { next(err); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { Router, Response, NextFunction } from 'express';
|
import { Router, Response, NextFunction } from 'express';
|
||||||
import { body, validationResult } from 'express-validator';
|
import { body, validationResult } from 'express-validator';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import prisma from '../lib/prisma';
|
||||||
import { authenticate, requireRole, AuthRequest } from '../middleware/auth';
|
import { authenticate, requireRole, AuthRequest } from '../middleware/auth';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
// ─── VOLUNTEER HOURS ─────────────────────────────────────────────────────────
|
// ─── VOLUNTEER HOURS ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: owl_stream
|
POSTGRES_DB: owl_stream
|
||||||
POSTGRES_USER: owl_user
|
POSTGRES_USER: owl_user
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-owl_secure_password}
|
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD must be set in .env}
|
||||||
volumes:
|
volumes:
|
||||||
- owl_pg_data:/var/lib/postgresql/data
|
- owl_pg_data:/var/lib/postgresql/data
|
||||||
networks:
|
networks:
|
||||||
@ -23,6 +23,10 @@ services:
|
|||||||
options:
|
options:
|
||||||
max-size: "10m"
|
max-size: "10m"
|
||||||
max-file: "3"
|
max-file: "3"
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 256M
|
||||||
|
|
||||||
owl-backend:
|
owl-backend:
|
||||||
build:
|
build:
|
||||||
@ -34,11 +38,11 @@ services:
|
|||||||
owl-db:
|
owl-db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgresql://owl_user:${DB_PASSWORD:-owl_secure_password}@owl-db:5432/owl_stream
|
DATABASE_URL: postgresql://owl_user:${DB_PASSWORD:?DB_PASSWORD required}@owl-db:5432/owl_stream
|
||||||
JWT_SECRET: ${JWT_SECRET:-owl_jwt_secret_change_me}
|
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET required}
|
||||||
PORT: 3020
|
PORT: 3020
|
||||||
HOST: 0.0.0.0
|
HOST: 0.0.0.0
|
||||||
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-http://localhost:8089}
|
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-https://owls.bizzle.cloud,http://localhost:8089}
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:3020:3020"
|
- "127.0.0.1:3020:3020"
|
||||||
networks:
|
networks:
|
||||||
@ -71,6 +75,7 @@ services:
|
|||||||
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://127.0.0.1:3020}
|
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://127.0.0.1:3020}
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8110:8089"
|
- "127.0.0.1:8110:8089"
|
||||||
|
- "172.18.0.1:8110:8089"
|
||||||
networks:
|
networks:
|
||||||
- owl-network
|
- owl-network
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user