M1.5 frontend: admin Settings UI + config store + register gating
CI & Build / Python lint (push) Successful in 3s
CI & Build / Python tests (push) Successful in 8s
CI & Build / TypeScript typecheck (push) Successful in 10s
CI & Build / Build & push image (push) Successful in 31s

- config store (public /api/config: site_name, allow_registration, version),
  loaded in the router guard; site name drives the board header.
- session User gains is_admin.
- /settings route with requiresAdmin guard + admin-only gear link in the header;
  SettingsView renders grouped typed fields (text/number/toggle), saves via
  PATCH /api/settings with saved/error feedback, refreshes public config.
- Register link hidden + /register route blocked when registration is closed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-19 18:28:56 -04:00
co-authored by Claude Opus 4.8
parent b46bda38ee
commit 68f7b260de
7 changed files with 208 additions and 3 deletions
+17 -1
View File
@@ -1,5 +1,6 @@
import { createRouter, createWebHistory } from "vue-router";
import { useSessionStore } from "../stores/session";
import { useConfigStore } from "../stores/config";
const router = createRouter({
history: createWebHistory(),
@@ -22,6 +23,12 @@ const router = createRouter({
component: () => import("../views/BoardView.vue"),
meta: { requiresAuth: true },
},
{
path: "/settings",
name: "settings",
component: () => import("../views/SettingsView.vue"),
meta: { requiresAuth: true, requiresAdmin: true },
},
{
path: "/login",
name: "login",
@@ -39,13 +46,22 @@ const router = createRouter({
router.beforeEach(async (to) => {
const session = useSessionStore();
// Resolve the current user once, lazily, before the first guarded navigation.
const config = useConfigStore();
await config.load();
if (!session.loaded) {
await session.fetchMe();
}
if (to.meta.requiresAuth && !session.user) {
return { name: "login", query: to.fullPath !== "/" ? { redirect: to.fullPath } : undefined };
}
if (to.meta.requiresAdmin && !session.user?.is_admin) {
return { name: "board" };
}
// Registration closed: keep people out of the register screen (the very first
// account is still creatable because allow_registration defaults to true).
if (to.name === "register" && !config.allowRegistration) {
return { name: "login" };
}
if (to.meta.guestOnly && session.user) {
return { name: "board" };
}