diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..b204567 --- /dev/null +++ b/.env.test @@ -0,0 +1,45 @@ +# ── Domain ──────────────────────────────────────────────────────────────────── +# Public domain Caddy will serve and obtain a TLS certificate for. +DOMAIN=my-event.example.com + +# ── App server ──────────────────────────────────────────────────────────────── +APP_PORT=3000 + +# ── Database ────────────────────────────────────────────────────────────────── +DATABASE_URL=postgres://eventsnap:secret@db:5432/eventsnap +POSTGRES_USER=eventsnap +POSTGRES_PASSWORD=secret +POSTGRES_DB=eventsnap + +# ── Authentication ──────────────────────────────────────────────────────────── +# Generate with: openssl rand -hex 64 +JWT_SECRET=change_me_to_a_random_64_byte_hex_string +SESSION_EXPIRY_DAYS=30 + +# Admin dashboard password (bcrypt hash). +# Generate with: htpasswd -bnBC 12 "" yourpassword | tr -d ':\n' +ADMIN_PASSWORD_HASH=$2y$12$placeholder_replace_me + +# ── Event ───────────────────────────────────────────────────────────────────── +EVENT_NAME=Max & Maria's Wedding +EVENT_SLUG=max-maria-2026 + +# ── Storage ─────────────────────────────────────────────────────────────────── +MEDIA_PATH=/media + +# ── Upload limits ───────────────────────────────────────────────────────────── +DEFAULT_MAX_IMAGE_SIZE_MB=20 +DEFAULT_MAX_VIDEO_SIZE_MB=500 + +# ── Rate limiting ───────────────────────────────────────────────────────────── +DEFAULT_UPLOAD_RATE_PER_HOUR=10 +DEFAULT_FEED_RATE_PER_MIN=60 +DEFAULT_EXPORT_RATE_PER_DAY=3 + +# ── Capacity ────────────────────────────────────────────────────────────────── +DEFAULT_ESTIMATED_GUEST_COUNT=100 +# Fraction of total storage that triggers the "low storage" warning (0.0–1.0) +DEFAULT_QUOTA_TOLERANCE=0.75 + +# ── Workers ─────────────────────────────────────────────────────────────────── +COMPRESSION_WORKER_CONCURRENCY=2 diff --git a/frontend/src/lib/components/CameraCapture.svelte b/frontend/src/lib/components/CameraCapture.svelte new file mode 100644 index 0000000..67698e6 --- /dev/null +++ b/frontend/src/lib/components/CameraCapture.svelte @@ -0,0 +1,238 @@ + + +
+ +
+ {#if error} +
+
+ + + +

{error}

+ +
+
+ {:else} + + + + {#if recording} +
+
+ {formatRecordingTime(recordingTime)} +
+ {/if} + {/if} +
+ + + {#if !error} +
+ + + + + {#if recording} + + {:else} + + {/if} + + + {#if recording} +
+ {:else} + + {/if} +
+ + + {#if !recording} +
+ +
+ {/if} + {/if} +
+ + + diff --git a/frontend/src/routes/upload/+page.svelte b/frontend/src/routes/upload/+page.svelte index 1368d2e..e6205ec 100644 --- a/frontend/src/routes/upload/+page.svelte +++ b/frontend/src/routes/upload/+page.svelte @@ -3,11 +3,13 @@ import { getToken } from '$lib/auth'; import { addToQueue, loadQueue } from '$lib/upload-queue'; import UploadQueue from '$lib/components/UploadQueue.svelte'; + import CameraCapture from '$lib/components/CameraCapture.svelte'; import { onMount } from 'svelte'; let caption = $state(''); let hashtags = $state(''); let fileInput: HTMLInputElement; + let showCamera = $state(false); onMount(() => { if (!getToken()) { @@ -30,8 +32,23 @@ hashtags = ''; if (fileInput) fileInput.value = ''; } + + async function handleCapture(blob: Blob, type: 'photo' | 'video') { + const ext = type === 'photo' ? 'jpg' : blob.type.includes('mp4') ? 'mp4' : 'webm'; + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const fileName = `${type}_${timestamp}.${ext}`; + const file = new File([blob], fileName, { type: blob.type }); + await addToQueue(file, caption, hashtags); + } +{#if showCamera} + (showCamera = false)} + /> +{/if} +
@@ -40,23 +57,39 @@
- +
+ + + + + +