From 8fa9aa516e61fa7d6464db7f75b55319e2960776 Mon Sep 17 00:00:00 2001 From: space Date: Sun, 26 Jul 2026 15:00:08 +0200 Subject: [PATCH 1/2] fix(ui): use app icon in header Co-Authored-By: Codex --- TODO.md | 2 +- frontend/src/components/Layout.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index f2db8051..fcd0eada 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,7 @@ ## UI Related - [ ] Fix Theming to be more professional (use frontend themeing skill & make it look like a professional product) - +- [x] replace emoji in the top left with the actual icon ## Future BS - [ ] CPU, MEM, NET, DISK sentinal first-installed on EC2s to expose a backend for the user's ui to hit, THROUGH A BACKEND PROXY ROUTE so we can cache and rate limit the requests. This will allow for a better dashboard and better stats for the user to see. We'll use an obscure port for the sentinal so that we dont hit any other services the user may have. Sentinal is our own little C program exposing the stats we need, seperate repo for that thing tho! diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 57e63331..306b405a 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -3,7 +3,7 @@ import { Link, useLocation, useNavigate } from "react-router-dom"; import { useAuth } from "../hooks/useAuth"; import { ThemePreference, useTheme } from "../hooks/useTheme"; import { api } from "../services/api"; -import { Rocket, Sun, Moon, Monitor } from "lucide-react"; +import { Sun, Moon, Monitor } from "lucide-react"; const themeOptions: { value: ThemePreference; label: string; Icon: typeof Sun }[] = [ { value: "light", label: "Light", Icon: Sun }, @@ -42,7 +42,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
- PR Previews + PR Previews
-- 2.39.5 From a1c7ad71202a0e521ebca8412820ca5fd99dfeda Mon Sep 17 00:00:00 2001 From: space Date: Sun, 26 Jul 2026 17:06:05 +0200 Subject: [PATCH 2/2] feat(setup): add ability to skip setup wizard and update user model --- backend/src/index.ts | 3 +- backend/src/routes/api/user.ts | 12 ++++++ backend/src/routes/auth.ts | 14 ++++++- frontend/src/hooks/useAuth.ts | 1 + frontend/src/pages/SetupWizard.tsx | 41 +++++++++++++++---- frontend/src/services/api.ts | 1 + .../migration.sql | 1 + prisma/schema.prisma | 1 + 8 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 prisma/migrations/20260726170000_setup_skipped/migration.sql diff --git a/backend/src/index.ts b/backend/src/index.ts index dd26f24d..6346aa88 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -18,7 +18,7 @@ import { runWebhookReconciliation } from "./services/webhookReconcile"; import { loginHandler, logoutHandler, meHandler, setupStatusHandler, firstUserHandler } from "./routes/auth"; import { webhookHandler } from "./routes/webhook"; -import { getUserSettings, updateUsername, updatePassword, updateGitea, updateAws, getWebhookSecret, regenerateWebhookSecret } from "./routes/api/user"; +import { getUserSettings, updateUsername, updatePassword, updateGitea, updateAws, skipSetupWizard, getWebhookSecret, regenerateWebhookSecret } from "./routes/api/user"; import { listRepos, saveRepoConfig, toggleRepoEnabled, getRepoConfig } from "./routes/api/repos"; import { listPreviews, getPreview, stopPreviewRoute, rebuildPreviewRoute, previewLogsWs, previewAppLogsWs } from "./routes/api/previews"; import { getStats } from "./routes/api/stats"; @@ -66,6 +66,7 @@ server.path("/", (path) => path .http("PATCH", "/api/user/password", (http) => http.onRequest(updatePassword)) .http("PUT", "/api/user/gitea", (http) => http.onRequest(updateGitea)) .http("PUT", "/api/user/aws", (http) => http.onRequest(updateAws)) + .http("POST", "/api/user/setup/skip", (http) => http.onRequest(skipSetupWizard)) .http("GET", "/api/user/webhook-secret", (http) => http.onRequest(getWebhookSecret)) .http("POST", "/api/user/webhook-secret/regenerate", (http) => http.onRequest(regenerateWebhookSecret)) ); diff --git a/backend/src/routes/api/user.ts b/backend/src/routes/api/user.ts index 469bbc57..117f6f75 100644 --- a/backend/src/routes/api/user.ts +++ b/backend/src/routes/api/user.ts @@ -181,6 +181,18 @@ export async function updateAws(ctr: any) { return makeResponse({ ctr, content: { code: 200, message: `Connected as ${validation.arn}`, data: { arn: validation.arn } } }); } +export async function skipSetupWizard(ctr: any) { + const user = requireAuth(ctr); + if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } }); + + await prisma.user.update({ + where: { id: user.id }, + data: { setupSkipped: true }, + }); + + return makeResponse({ ctr, content: { code: 200, message: "Setup wizard skipped" } }); +} + export async function getWebhookSecret(ctr: any) { const user = requireAuth(ctr); if (!user) return makeResponse({ ctr, content: { code: 401, message: ERROR_MESSAGES.UNAUTHORIZED.message } }); diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index 7ad9a7c3..410ce96a 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -10,6 +10,17 @@ const log = createLogger("AUTH_ROUTE"); const COOKIE_NAME = "pp_session"; const COOKIE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000; +function isSetupComplete(user: { + setupSkipped: boolean; + giteaInstanceUrl: string | null; + giteaPAT: string | null; + awsAccessKeyId: string | null; + awsSecretAccessKey: string | null; + awsRegion: string | null; +}) { + return user.setupSkipped || !!(user.giteaInstanceUrl && user.giteaPAT && user.awsAccessKeyId && user.awsSecretAccessKey && user.awsRegion); +} + export async function loginHandler(ctr: any) { let body: any; try { @@ -82,7 +93,8 @@ export async function meHandler(ctr: any) { awsAccessKeyId: user.awsAccessKeyId ? "****" : null, awsRegion: user.awsRegion, awsConfigured: !!(user.awsAccessKeyId && user.awsSecretAccessKey && user.awsRegion), - setupComplete: !!(user.giteaInstanceUrl && user.giteaPAT && user.awsAccessKeyId && user.awsSecretAccessKey && user.awsRegion), + setupSkipped: user.setupSkipped, + setupComplete: isSetupComplete(user), }, }, }); diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts index 2db1583b..eac4af1a 100644 --- a/frontend/src/hooks/useAuth.ts +++ b/frontend/src/hooks/useAuth.ts @@ -6,6 +6,7 @@ export interface AuthUser { username: string; isAdmin: boolean; isFounder: boolean; + setupSkipped: boolean; setupComplete: boolean; giteaUsername: string | null; giteaInstanceUrl: string | null; diff --git a/frontend/src/pages/SetupWizard.tsx b/frontend/src/pages/SetupWizard.tsx index af7a144e..97b4619e 100644 --- a/frontend/src/pages/SetupWizard.tsx +++ b/frontend/src/pages/SetupWizard.tsx @@ -44,7 +44,32 @@ export function SetupWizard() { }, [step, user?.id]); const next = () => setStep(s => Math.min(s + 1, STEPS.length - 1)); - const skip = () => navigate("/"); + const skip = async () => { + setSaving(true); + const res = await api.user.skipSetup(); + setSaving(false); + if (res.ok) { + await refresh(); + navigate("/"); + } else { + toast.error(res.message || "Failed to skip setup"); + } + }; + const leaveWizard = async (path: string) => { + if (user?.setupComplete) { + navigate(path); + return; + } + setSaving(true); + const res = await api.user.skipSetup(); + setSaving(false); + if (res.ok) { + await refresh(); + navigate(path); + } else { + toast.error(res.message || "Failed to finish setup"); + } + }; const handleGiteaNext = async () => { setSaving(true); @@ -112,8 +137,8 @@ export function SetupWizard() { -
@@ -211,10 +236,10 @@ export function SetupWizard() { Go to the Repos page to enable previews for a repository. PP will automatically register the webhook.

- +
@@ -227,8 +252,8 @@ export function SetupWizard() {

Open a pull request on an enabled repo and PP will provision a preview automatically.

- )} diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index a20fb55d..b947a2cc 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -37,6 +37,7 @@ export const api = { request("PUT", "/user/gitea", data), updateAws: (data: { awsAccessKeyId: string; awsSecretAccessKey: string; awsRegion: string }) => request("PUT", "/user/aws", data), + skipSetup: () => request("POST", "/user/setup/skip"), getWebhookSecret: () => request("GET", "/user/webhook-secret"), regenerateWebhookSecret: () => request("POST", "/user/webhook-secret/regenerate"), }, diff --git a/prisma/migrations/20260726170000_setup_skipped/migration.sql b/prisma/migrations/20260726170000_setup_skipped/migration.sql new file mode 100644 index 00000000..55bf12d8 --- /dev/null +++ b/prisma/migrations/20260726170000_setup_skipped/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "User" ADD COLUMN "setupSkipped" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 25bec2d3..888e80e9 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -36,6 +36,7 @@ model User { passwordHash String @db.VarChar(256) isAdmin Boolean @default(false) isFounder Boolean @default(false) + setupSkipped Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt -- 2.39.5