enable dev|prod envs with simpler env vars system
This commit is contained in:
@@ -29,6 +29,9 @@
|
||||
"vite": "8.0.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"pocketbase": "^0.27.0"
|
||||
"@hiseb/confetti": "^2.0.2",
|
||||
"chart.js": "^4.4.0",
|
||||
"pocketbase": "^0.27.0",
|
||||
"qrcode": "^1.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
+15
-2
@@ -1,6 +1,19 @@
|
||||
import { defineEnvVars } from '@sveltejs/kit/hooks';
|
||||
|
||||
// Default when the env var isn't set, so a missing value never crashes startup.
|
||||
const withDefault = (value: string) => ({
|
||||
'~standard': {
|
||||
version: 1,
|
||||
vendor: 'famchamp',
|
||||
validate: (v: unknown) => ({ value: typeof v === 'string' && v ? v : value })
|
||||
}
|
||||
} as const);
|
||||
|
||||
export const variables = defineEnvVars({
|
||||
DEBUG_RECORD_ID: {},
|
||||
PUBLIC_PB_URL: { public: true }
|
||||
// SSR → Hono proxy (loopback, proxy runs on the same host as SSR).
|
||||
PROXY_URL: { public: true, schema: withDefault('http://127.0.0.1:3456') },
|
||||
SERVER_IP: { public: true, schema: withDefault('192.168.1.225') },
|
||||
// PB superuser creds (server-only).
|
||||
PB_EMAIL: { public: false, schema: withDefault('debug@famchamp.dev') },
|
||||
PB_PASSWORD: { public: false, schema: withDefault('debug123') }
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
const BASE_URL =
|
||||
typeof window === 'undefined'
|
||||
? typeof process !== 'undefined'
|
||||
? process.env.PROXY_URL ||
|
||||
`http://${process.env.SERVER_IP || '192.168.1.225'}:${process.env.PROXY_PORT || '3456'}`
|
||||
: ''
|
||||
: '';
|
||||
// Client-only. All /api calls go same-origin (vite proxy in dev, nginx in
|
||||
// prod). Server-side (SSR) calls use PROXY_URL from $app/env/public instead.
|
||||
const BASE_URL = '';
|
||||
|
||||
async function memberFetch<T = unknown>(
|
||||
method: string,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import PocketBase from 'pocketbase';
|
||||
import { PUBLIC_PB_URL } from '$app/env/public';
|
||||
|
||||
export const pb = new PocketBase(PUBLIC_PB_URL);
|
||||
import { SERVER_IP } from '$app/env/public';
|
||||
const PB_ENDPOINT = import.meta.env.PROD ? '/pb' : `http://${SERVER_IP}:8090`;
|
||||
export const pb = new PocketBase(PB_ENDPOINT);
|
||||
pb.autoCancellation(false);
|
||||
|
||||
export function initPbFromCookie() {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
import { PROXY_URL } from '$lib/server/env';
|
||||
|
||||
const HONO_URL = PROXY_URL;
|
||||
import { PROXY_URL } from '$app/env/public';
|
||||
|
||||
export function getSession(event: RequestEvent) {
|
||||
return event.locals.session;
|
||||
@@ -17,7 +15,7 @@ export function requireAuth(event: RequestEvent) {
|
||||
}
|
||||
|
||||
export async function signup(email: string, password: string, famName: string, parentName?: string) {
|
||||
const res = await fetch(`${HONO_URL}/api/admin/signup`, {
|
||||
const res = await fetch(`${PROXY_URL}/api/admin/signup`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password, famName, parentName }),
|
||||
@@ -29,7 +27,7 @@ export async function signup(email: string, password: string, famName: string, p
|
||||
|
||||
export async function login(email: string, password: string) {
|
||||
console.log(email);
|
||||
const res = await fetch(`${HONO_URL}/api/admin/login`, {
|
||||
const res = await fetch(`${PROXY_URL}/api/admin/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password }),
|
||||
@@ -40,7 +38,7 @@ export async function login(email: string, password: string) {
|
||||
}
|
||||
|
||||
export async function joinMember(inviteCode: string, name: string, deviceToken: string) {
|
||||
const res = await fetch(`${HONO_URL}/api/members/join`, {
|
||||
const res = await fetch(`${PROXY_URL}/api/members/join`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ inviteCode, name, deviceToken }),
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { PROXY_URL } from '$lib/server/env';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
|
||||
const HONO_URL = PROXY_URL;
|
||||
import { PROXY_URL } from '$app/env/public';
|
||||
|
||||
function sessionHeaders(event: RequestEvent): Record<string, string> {
|
||||
const s = event.locals.session;
|
||||
@@ -19,7 +17,7 @@ async function request(
|
||||
body?: unknown,
|
||||
headers?: Record<string, string>
|
||||
) {
|
||||
const res = await fetch(`${HONO_URL}${path}`, {
|
||||
const res = await fetch(`${PROXY_URL}${path}`, {
|
||||
method,
|
||||
headers: headers || { 'Content-Type': 'application/json' },
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { PB_ENDPOINT, PB_EMAIL, PB_PASSWORD } from './env';
|
||||
import { PB_EMAIL, PB_PASSWORD } from '$app/env/private';
|
||||
import { SERVER_IP } from '$app/env/public';
|
||||
export const PB_ENDPOINT = import.meta.env.PROD ? '/pb' : `http://${SERVER_IP}:8090`;
|
||||
|
||||
let token: string | null = null;
|
||||
let tokenExpiry = 0;
|
||||
|
||||
@@ -67,7 +67,7 @@ class FamStore {
|
||||
|
||||
private initPromise: Promise<void> | null = null;
|
||||
|
||||
async init(famId: string) {
|
||||
async init(famId: string, initialFam?: Fam) {
|
||||
if (this.initialized && this.famId === famId) return;
|
||||
|
||||
// Wait for any in-flight init to finish first
|
||||
@@ -82,8 +82,11 @@ class FamStore {
|
||||
|
||||
this.initPromise = (async () => {
|
||||
try {
|
||||
// fams is superadmin-only (non-realtime, one-way writes). It is always
|
||||
// fetched server-side by the layout load and passed in — never via the
|
||||
// unauthenticated client PB SDK.
|
||||
this.fam = initialFam || this.fam || ({} as Fam);
|
||||
const [
|
||||
famRes,
|
||||
membersRes,
|
||||
templatesRes,
|
||||
assignedRes,
|
||||
@@ -93,7 +96,6 @@ class FamStore {
|
||||
rewardsRes,
|
||||
seasonsRes
|
||||
] = await Promise.all([
|
||||
pb.collection('fams').getOne(famId) as Promise<Fam>,
|
||||
pb.collection('members').getFullList({ filter: `famId = '${famId}'` }) as Promise<
|
||||
Member[]
|
||||
>,
|
||||
@@ -119,7 +121,6 @@ class FamStore {
|
||||
Season[]
|
||||
>
|
||||
]);
|
||||
this.fam = famRes;
|
||||
this.members = membersRes;
|
||||
this.templates = templatesRes;
|
||||
this.assigned = assignedRes;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PROXY_URL } from '$lib/server/env';
|
||||
import { PROXY_URL } from '$app/env/public';
|
||||
import { pbAdmin } from '$lib/server/pb-admin';
|
||||
|
||||
const HONO_URL = PROXY_URL;
|
||||
|
||||
@@ -77,5 +78,16 @@ export async function load(event) {
|
||||
}
|
||||
}
|
||||
|
||||
return { session, isParent, role: session?.role || 'child', famId, chat, deviceToken };
|
||||
return {
|
||||
session,
|
||||
isParent,
|
||||
role: session?.role || 'child',
|
||||
famId,
|
||||
chat,
|
||||
deviceToken,
|
||||
// fams is superadmin-only (non-realtime). Fetched server-side for both roles.
|
||||
fam: famId
|
||||
? await pbAdmin.getOne('fams', famId).catch(() => null)
|
||||
: null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
onMount(() => {
|
||||
initPbFromCookie();
|
||||
if (page.data.famId) famStore.init(page.data.famId);
|
||||
if (page.data.famId) famStore.init(page.data.famId, page.data.fam);
|
||||
const chat = page.data.chat;
|
||||
if (chat?.famId && chat?.actor) {
|
||||
chatStore.init({
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { hono } from '$lib/server/hono';
|
||||
import { memberApi } from '$lib/client/api';
|
||||
import { PROXY_URL } from '$lib/server/env';
|
||||
import { PROXY_URL } from '$app/env/public';
|
||||
|
||||
const HONO_URL = PROXY_URL;
|
||||
|
||||
@@ -101,7 +100,11 @@ export async function load(event) {
|
||||
|
||||
let chores: any = {};
|
||||
try {
|
||||
chores = await memberApi.myChores(deviceToken, data.famId);
|
||||
const choresRes = await fetch(`${HONO_URL}/api/members/my-chores`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-device-token': deviceToken, 'x-device-famid': data.famId }
|
||||
});
|
||||
chores = await choresRes.json();
|
||||
} catch {}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { hono } from '$lib/server/hono';
|
||||
import { PROXY_URL } from '$lib/server/env';
|
||||
import { PROXY_URL } from '$app/env/public';
|
||||
|
||||
const HONO_URL = PROXY_URL;
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { enhance } from '$app/forms';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { pb, initPbFromCookie } from '$lib/pocketbase';
|
||||
import { famStore } from '$lib/stores/fam.svelte';
|
||||
import { ViewHeader, CardGrid, Card, Button } from '$lib/components';
|
||||
import { COMMON_TIMEZONES } from '../../../../../../timezone.ts';
|
||||
@@ -11,8 +9,8 @@
|
||||
let { data } = $props();
|
||||
|
||||
// fam is sensitive (inviteCode, stripeCustomerId, featureFlags) — never in the
|
||||
// public famStore stream. This admin-only page subscribes to `fams` with the
|
||||
// authenticated PB instance (pb_token cookie), page-local only.
|
||||
// public famStore stream. It is superadmin-only, fetched server-side by the
|
||||
// layout load. Writes go through form actions; no live fam subscription.
|
||||
let fam = $state(data.fam);
|
||||
let famSlug = $state(page.params.fam);
|
||||
|
||||
@@ -65,32 +63,6 @@
|
||||
let members = $state(famStore.initialized ? famStore.members : data.members || []);
|
||||
let deletingSeason = $state<any>(null);
|
||||
|
||||
function syncFamFromRecord(record: any) {
|
||||
fam = record;
|
||||
payday = Number(record.payday ?? 1);
|
||||
paydayTime = record.paydayTime || '18:00';
|
||||
timezone = record.timezone || 'auto';
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
initPbFromCookie();
|
||||
if (!fam?.id) return;
|
||||
try {
|
||||
await pb.collection('fams').subscribe(fam.id, ({ action, record }) => {
|
||||
if (action === 'update') syncFamFromRecord(record);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('fams subscribe failed:', e);
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (fam?.id)
|
||||
pb.collection('fams')
|
||||
.unsubscribe(fam.id)
|
||||
.catch(() => {});
|
||||
});
|
||||
|
||||
function copy(url: string) {
|
||||
navigator.clipboard.writeText(url);
|
||||
copied = true;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { pbAdmin } from '$lib/server/pb-admin';
|
||||
import { redirect, fail } from '@sveltejs/kit';
|
||||
import { PB_EMAIL, PB_PASSWORD } from '$lib/server/env';
|
||||
import { PB_EMAIL, PB_PASSWORD } from '$app/env/private';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies }) => {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { DEBUG_RECORD_ID } from '$app/env/private';
|
||||
export async function load() {
|
||||
return {
|
||||
recordId: DEBUG_RECORD_ID
|
||||
};
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { pb } from "$lib/pocketbase";
|
||||
let { data } = $props();
|
||||
let records = $state(null);
|
||||
$effect(async () => {
|
||||
records = await pb.collection('debug').getOne(data.recordId);
|
||||
return await pb.collection('debug').subscribe(data.recordId, ({ action, record }) => {
|
||||
records = record
|
||||
// if (action === 'create') {
|
||||
// todos = [...todos, record];
|
||||
// }
|
||||
|
||||
// if (action === 'update') {
|
||||
// todos = todos.map((t) =>
|
||||
// t.id === record.id ? record : t
|
||||
// );
|
||||
// }
|
||||
|
||||
// if (action === 'delete') {
|
||||
// todos = todos.filter((t) =>
|
||||
// t.id !== record.id
|
||||
// );
|
||||
// }
|
||||
});
|
||||
});
|
||||
|
||||
async function incrementHono() {
|
||||
try {
|
||||
const response = await fetch('/api/increment-hono', { method: 'POST' });
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>Debug Dashboard</h1>
|
||||
|
||||
<div class="counters">
|
||||
<div class="card">
|
||||
<h2>Hono</h2>
|
||||
{#if records}
|
||||
<p class="value">{records.hono_count}</p>
|
||||
{/if}
|
||||
<button onclick={incrementHono}>+1 Hono</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
.counters {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
min-width: 200px;
|
||||
}
|
||||
.value {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
button {
|
||||
padding: 0.5rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { setDeviceTokenCookie } from '$lib/server/auth';
|
||||
import { PROXY_URL } from '$lib/server/env';
|
||||
import { PROXY_URL } from '$app/env/public';
|
||||
|
||||
const HONO_URL = PROXY_URL;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { SERVER_IP, FRONTEND_PORT, PROXY_PORT } from '../config.ts';
|
||||
|
||||
export default defineConfig(() => {
|
||||
return {
|
||||
@@ -25,11 +24,13 @@ export default defineConfig(() => {
|
||||
fs: {
|
||||
allow: ['.', './node_modules', '../node_modules']
|
||||
},
|
||||
allowedHosts: [SERVER_IP],
|
||||
port: FRONTEND_PORT,
|
||||
// Dev only: allow access via any host/LAN IP without hardcoding it.
|
||||
allowedHosts: true,
|
||||
port: 2080,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: `http://${SERVER_IP}:${PROXY_PORT}`,
|
||||
// The vite dev server and Hono proxy run on the same host.
|
||||
target: `http://127.0.0.1:3456`,
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user