Add full Docker deployment: setup.sh, update.sh, healthcheck, TURN support
- setup.sh: interactive/non-interactive one-shot installer (build, DB healthcheck, migrate, seed, start), idempotent secret generation, NPM reverse-proxy network auto-detection and optional join, optional AUTO_UPDATE cron install. - update.sh: release-tag-gated updates only (never bare main), DB backup with retention before every update, lock file against concurrent runs, automatic code rollback on failed post-update healthcheck. - Dockerfile: multi-stage build, non-root user, built-in HEALTHCHECK against the new /api/health route, wholesale COPY so new source dirs (e.g. scripts/) never silently go missing at runtime. - docker-compose.yml: internal anouma-network (configurable), named volume for Postgres, app depends_on postgres healthy, no unnecessary published ports; docker-compose.override.yml.example documents joining an existing NPM network without ever touching NPM itself. - Fix host-detection: isHost was Boolean(user), wrongly granting host privileges to logged-in customers; now checks user.collection === "users". - Wire configurable STUN/TURN servers through to the WebRTC client (lib/meeting/iceServers.ts) so a TURN server can be added later via env vars only, no code changes. - DEPLOYMENT.md, updated README.md and .env.example documenting the whole flow: NPM integration, env vars, WebRTC, updates, backups, rollback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import { VideoTile } from "./VideoTile";
|
||||
import { MEETING_SESSION_KEY, type StoredMeetingSession } from "./JoinForm";
|
||||
import type { ClientToServerMessage, ParticipantSummary, ServerToClientMessage } from "@/lib/meeting/protocol";
|
||||
|
||||
const ICE_SERVERS: RTCIceServer[] = [{ urls: "stun:stun.l.google.com:19302" }];
|
||||
const DEFAULT_ICE_SERVERS: RTCIceServer[] = [{ urls: "stun:stun.l.google.com:19302" }];
|
||||
|
||||
type RemoteEntry = ParticipantSummary & { stream: MediaStream | null };
|
||||
|
||||
@@ -48,7 +48,9 @@ export function CallRoom({ slug }: { slug: string }) {
|
||||
|
||||
const createPeerConnection = useCallback(
|
||||
(participantId: string, name: string, role: ParticipantSummary["role"], isInitiator: boolean) => {
|
||||
const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: sessionRef.current?.iceServers?.length ? sessionRef.current.iceServers : DEFAULT_ICE_SERVERS,
|
||||
});
|
||||
pcsRef.current.set(participantId, pc);
|
||||
|
||||
localStreamRef.current?.getTracks().forEach((track) => {
|
||||
|
||||
@@ -15,6 +15,7 @@ export type StoredMeetingSession = {
|
||||
name: string;
|
||||
eventTitle: string;
|
||||
eventSlug: string;
|
||||
iceServers: RTCIceServer[];
|
||||
};
|
||||
|
||||
export function JoinForm({ slug }: { slug: string }) {
|
||||
@@ -49,6 +50,7 @@ export function JoinForm({ slug }: { slug: string }) {
|
||||
name,
|
||||
eventTitle: data.eventTitle,
|
||||
eventSlug: slug,
|
||||
iceServers: data.iceServers,
|
||||
};
|
||||
sessionStorage.setItem(MEETING_SESSION_KEY, JSON.stringify(session));
|
||||
router.push(`/termine/${slug}/call`);
|
||||
|
||||
Reference in New Issue
Block a user