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
+38
View File
@@ -0,0 +1,38 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { api } from "../api/client";
interface PublicConfig {
site_name: string;
allow_registration: boolean;
version: string;
}
// Public, unauthenticated app config (site name, whether signups are open).
export const useConfigStore = defineStore("config", () => {
const siteName = ref("ThoughtSync");
const allowRegistration = ref(true);
const version = ref("");
const loaded = ref(false);
async function load(): Promise<void> {
if (loaded.value) return;
try {
const cfg = await api.get<PublicConfig>("/api/config");
siteName.value = cfg.site_name;
allowRegistration.value = cfg.allow_registration;
version.value = cfg.version;
} catch {
// Keep defaults if the config endpoint is unreachable.
} finally {
loaded.value = true;
}
}
async function reload(): Promise<void> {
loaded.value = false;
await load();
}
return { siteName, allowRegistration, version, loaded, load, reload };
});