Add proper login to Admin UI, replace Caddy basic_auth
All checks were successful
Deploy to Server / deploy (push) Successful in 1m55s

- iron-session for encrypted httpOnly session cookies
- POST /api/auth/login: verifies ADMIN_USERNAME/ADMIN_PASSWORD, sets session, returns API key
- POST /api/auth/logout: destroys session
- middleware.ts: protects all routes, redirects to /login if unauthenticated
- Login page: username + password form (no more browser popup)
- Sidebar: logout calls API route and clears session
- docker-compose.prod.yml: admin-ui reads /opt/rustycms/.env.admin
- deploy.yml: generates .env.admin from Gitea secrets
- Caddy: basic_auth removed from /admin* block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-03-15 21:49:32 +01:00
parent c644c08222
commit b432621919
13 changed files with 303 additions and 118 deletions

View File

@@ -2,11 +2,11 @@
import { useState, useMemo, useEffect } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { usePathname, useRouter } from "next/navigation";
import { Icon } from "@iconify/react";
import { useQueries, useQuery } from "@tanstack/react-query";
import { useTranslations } from "next-intl";
import { fetchCollections, fetchContentList, getApiKey, setApiKey } from "@/lib/api";
import { fetchCollections, fetchContentList, getApiKey, clearSession } from "@/lib/api";
import { LocaleSwitcher } from "./LocaleSwitcher";
const navLinkClass =
@@ -21,6 +21,7 @@ type SidebarProps = {
export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
const t = useTranslations("Sidebar");
const pathname = usePathname();
const router = useRouter();
const [search, setSearch] = useState("");
const [, setLogoutVersion] = useState(0);
const [mounted, setMounted] = useState(false);
@@ -237,10 +238,13 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
) : hasStoredKey ? (
<button
type="button"
onClick={() => {
setApiKey(null);
onClick={async () => {
clearSession();
setLogoutVersion((v) => v + 1);
onClose?.();
await fetch("/admin/api/auth/logout", { method: "POST" });
router.push("/login");
router.refresh();
}}
className={`${navLinkClass} w-full text-left text-gray-700 hover:bg-accent-100/80 hover:text-gray-900`}
>