Compare commits

..

8 Commits

Author SHA1 Message Date
6114997d5c fix: move tailscale setup to postStartCommand
Co-authored-by: Ona <no-reply@ona.com>
2026-03-24 22:57:15 +00:00
1596f2bedf fix: use http protocol for service ports (#97)
Co-authored-by: Ona <no-reply@ona.com>
2026-03-24 22:44:56 +00:00
b85109e2e2 feat: auto-login to tailscale in devcontainer (#96) 2026-03-24 22:20:34 +00:00
eb5149a500 fix: add --host to admin dashboard dev server (#95)
Bind Vite to all interfaces so port forwarding works in
Ona environments.

Co-authored-by: Ona <no-reply@ona.com>
2026-03-24 22:12:21 +00:00
02f519c29c dev: add service definitions for backend and dashboard (#94)
Co-authored-by: Ona <no-reply@ona.com>
2026-03-24 21:24:21 +00:00
59d14ee37b fix(admin-dashboard): redirect to login on session fetch failure (#93)
Wrap the session check in beforeLoad with a try/catch so
network errors, 404s, and other failures redirect to the
login page instead of showing an error boundary.

Co-authored-by: Ona <no-reply@ona.com>
2026-03-24 21:11:38 +00:00
9b0ac1cd4e feat: add admin dashboard app (#91)
* feat: add admin dashboard app

- React + Vite + TanStack Router + TanStack Query
- Auth with better-auth (login, session, admin guard)
- Source config management (WeatherKit credentials, user config)
- Feed query panel
- Location push card
- General settings with health check
- CORS middleware for cross-origin auth
- Disable CSRF check in dev mode
- Sonner toasts for mutation feedback

Co-authored-by: Ona <no-reply@ona.com>

* fix: use useQuery instead of getQueryData

Co-authored-by: Ona <no-reply@ona.com>

* refactor: remove backend changes from dashboard PR

Backend CORS/CSRF changes moved to #92.
Source registry removed (sources hardcoded in frontend).

Co-authored-by: Ona <no-reply@ona.com>

---------

Co-authored-by: Ona <no-reply@ona.com>
2026-03-23 00:31:34 +00:00
35c6371d48 fix(backend): add CORS and disable CSRF in dev (#92)
* fix(backend): add CORS middleware and disable CSRF in dev

- Add CORS middleware for /api/auth/* and global routes
- Disable better-auth CSRF origin check when NODE_ENV != production

Co-authored-by: Ona <no-reply@ona.com>

* fix: gate permissive CORS to dev only

In production, only origins listed in CORS_ORIGINS env
var are allowed. In dev, any origin is reflected back.

Co-authored-by: Ona <no-reply@ona.com>

---------

Co-authored-by: Ona <no-reply@ona.com>
2026-03-23 00:31:23 +00:00
6 changed files with 83 additions and 5 deletions

View File

@@ -11,7 +11,7 @@
"dockerfile": "Dockerfile"
},
"postCreateCommand": "bun install",
"postStartCommand": "./scripts/setup-git.sh && ./scripts/setup-nvim.sh",
"postStartCommand": "./scripts/setup-git.sh && ./scripts/setup-nvim.sh && ./scripts/setup-tailscale.sh",
// Features add additional features to your environment. See https://containers.dev/features
// Beware: features are not supported on all platforms and may have unintended side-effects.
"features": {

View File

@@ -17,3 +17,23 @@ services:
FORWARD_URL=$(gitpod environment port open 4983 --name drizzle-studio-server | sed 's|https://||')
echo "Drizzle Studio: https://local.drizzle.studio/?host=${FORWARD_URL}&port=443"
cd apps/aelis-backend && bunx drizzle-kit studio --host 0.0.0.0 --port 4983
aelis-backend:
name: Aelis Backend
description: Hono API server for aelis-backend (port 3000)
triggeredBy:
- manual
commands:
start: |
gitpod --context environment environment port open 3000 --name "Aelis Backend" --protocol http
cd apps/aelis-backend && bun run dev
admin-dashboard:
name: Admin Dashboard
description: Vite dev server for admin-dashboard (port 5174)
triggeredBy:
- manual
commands:
start: |
gitpod --context environment environment port open 5174 --name "Admin Dashboard" --protocol http
cd apps/admin-dashboard && bun run dev --host

View File

@@ -47,10 +47,15 @@ export const Route = createRoute({
getParentRoute: () => rootRoute,
id: "dashboard",
beforeLoad: async ({ context }) => {
const session = await context.queryClient.ensureQueryData({
let session: Awaited<ReturnType<typeof getSession>> | null = null
try {
session = await context.queryClient.ensureQueryData({
queryKey: ["session"],
queryFn: getSession,
})
} catch {
throw redirect({ to: "/login" })
}
if (!session?.user) {
throw redirect({ to: "/login" })
}

View File

@@ -16,6 +16,9 @@ export function createAuth(db: Database) {
provider: "pg",
schema,
}),
advanced: {
disableCSRFCheck: process.env.NODE_ENV !== "production",
},
emailAndPassword: {
enabled: true,
},

View File

@@ -1,4 +1,5 @@
import { Hono } from "hono"
import { cors } from "hono/cors"
import { registerAdminHttpHandlers } from "./admin/http.ts"
import { createRequireAdmin } from "./auth/admin-middleware.ts"
@@ -50,6 +51,34 @@ function main() {
const app = new Hono()
const isDev = process.env.NODE_ENV !== "production"
const allowedOrigins = process.env.CORS_ORIGINS?.split(",").map((o) => o.trim()) ?? []
function resolveOrigin(origin: string): string | undefined {
if (isDev) return origin
return allowedOrigins.includes(origin) ? origin : undefined
}
app.use(
"/api/auth/*",
cors({
origin: resolveOrigin,
allowHeaders: ["Content-Type", "Authorization"],
allowMethods: ["POST", "GET", "OPTIONS"],
exposeHeaders: ["Content-Length"],
maxAge: 600,
credentials: true,
}),
)
app.use(
"*",
cors({
origin: resolveOrigin,
credentials: true,
}),
)
app.get("/health", (c) => c.json({ status: "ok" }))
const authSessionMiddleware = createRequireSession(auth)

21
scripts/setup-tailscale.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
# Tailscale setup script
# Authenticates with Tailscale if TS_AUTH_KEY is set and Tailscale is not already logged in
set -e
if [ -z "$TS_AUTH_KEY" ]; then
echo "TS_AUTH_KEY is not set, skipping Tailscale login."
exit 0
fi
STATUS=$(tailscale status 2>&1 || true)
if echo "$STATUS" | grep -qi "logged out\|stopped"; then
echo "Tailscale is not authenticated. Logging in..."
sudo tailscale up --accept-routes --auth-key="$TS_AUTH_KEY"
echo "Tailscale login complete."
else
echo "Tailscale is already authenticated, skipping."
fi