diff --git a/app/donate/page.tsx b/app/donate/page.tsx
index ff35666..033b92b 100644
--- a/app/donate/page.tsx
+++ b/app/donate/page.tsx
@@ -43,6 +43,15 @@ export default async function DonatePage() {
+ {/* Coming soon notice */}
+
+
🚧
+
+
Online Donations Coming Soon
+
We're setting up secure payment processing. In the meantime, please visit ccfriendsofwildlife.org to donate directly.
+
+
+
{/* Impact banner */}
{[
diff --git a/backend/src/lib/prisma.ts b/backend/src/lib/prisma.ts
new file mode 100644
index 0000000..4e54f7a
--- /dev/null
+++ b/backend/src/lib/prisma.ts
@@ -0,0 +1,5 @@
+import { PrismaClient } from '@prisma/client';
+
+const prisma = new PrismaClient();
+
+export default prisma;
diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts
index d7c37d9..332b60c 100644
--- a/backend/src/routes/auth.ts
+++ b/backend/src/routes/auth.ts
@@ -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(),
diff --git a/backend/src/routes/burrows.ts b/backend/src/routes/burrows.ts
index 3380bff..a6363ef 100644
--- a/backend/src/routes/burrows.ts
+++ b/backend/src/routes/burrows.ts
@@ -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) => {
diff --git a/backend/src/routes/donations.ts b/backend/src/routes/donations.ts
index 1354a17..08947a0 100644
--- a/backend/src/routes/donations.ts
+++ b/backend/src/routes/donations.ts
@@ -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
diff --git a/backend/src/routes/events.ts b/backend/src/routes/events.ts
index 5fae125..35bca93 100644
--- a/backend/src/routes/events.ts
+++ b/backend/src/routes/events.ts
@@ -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) => {
diff --git a/backend/src/routes/sightings.ts b/backend/src/routes/sightings.ts
index 8eac2bf..617c30e 100644
--- a/backend/src/routes/sightings.ts
+++ b/backend/src/routes/sightings.ts
@@ -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) => {
diff --git a/backend/src/routes/stats.ts b/backend/src/routes/stats.ts
index 3643502..6200a75 100644
--- a/backend/src/routes/stats.ts
+++ b/backend/src/routes/stats.ts
@@ -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) => {
diff --git a/backend/src/routes/streams.ts b/backend/src/routes/streams.ts
index 2a22db2..095c046 100644
--- a/backend/src/routes/streams.ts
+++ b/backend/src/routes/streams.ts
@@ -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); }
});
diff --git a/backend/src/routes/volunteer.ts b/backend/src/routes/volunteer.ts
index af9caac..9b0d910 100644
--- a/backend/src/routes/volunteer.ts
+++ b/backend/src/routes/volunteer.ts
@@ -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 ─────────────────────────────────────────────────────────
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 17d3b31..3be4588 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -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: