支持固定登录启动模式

This commit is contained in:
zhangshun
2026-05-25 10:38:18 +08:00
parent c2ac560527
commit 318105443a
6 changed files with 93 additions and 6 deletions

View File

@@ -1,6 +1,7 @@
import bcrypt from "bcryptjs";
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { z } from "zod";
import { config } from "./config.js";
import { db } from "./db.js";
import { AuthUser, UserRow } from "./types.js";
@@ -60,7 +61,57 @@ function normalizeExpiresAt(value: string | null | undefined) {
return clean.length === 10 ? `${clean}T23:59:59` : clean;
}
async function ensureFixedLoginUser() {
const username = config.fixedLogin.username.trim() || "viewer";
const existing = db.prepare(`
SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
FROM users
WHERE username = ?
`).get(username) as unknown as UserRow | undefined;
if (existing) {
db.prepare(`
UPDATE users
SET role = ?, enabled = 1, expires_at = NULL, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`).run(config.fixedLogin.role, existing.id);
return db.prepare(`
SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
FROM users
WHERE id = ?
`).get(existing.id) as unknown as UserRow;
}
const passwordHash = await bcrypt.hash(`fixed-${Date.now()}-${Math.random()}`, 10);
const result = db.prepare(`
INSERT INTO users (username, password_hash, role, enabled, expires_at)
VALUES (?, ?, ?, 1, NULL)
`).run(username, passwordHash, config.fixedLogin.role);
return db.prepare(`
SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
FROM users
WHERE id = ?
`).get(Number(result.lastInsertRowid)) as unknown as UserRow;
}
function authUser(row: Pick<UserRow, "id" | "username" | "role">): AuthUser {
return {
id: row.id,
username: row.username,
role: row.role
};
}
export async function authRoutes(app: FastifyInstance) {
app.get("/api/auth/bootstrap", async () => {
if (config.authMode !== "fixed") {
return { mode: "login" };
}
const user = await ensureFixedLoginUser();
const payload = authUser(user);
const token = app.jwt.sign(payload);
return { mode: "fixed", token, user: payload };
});
app.post("/api/auth/register", async (request, reply) => {
const body = registerSchema.parse(request.body);
const passwordHash = await bcrypt.hash(body.password, 10);
@@ -98,8 +149,9 @@ export async function authRoutes(app: FastifyInstance) {
return reply.code(403).send({ message: "账号授权已过期" });
}
const token = app.jwt.sign({ id: user.id, username: user.username, role: user.role });
return { token, user: { id: user.id, username: user.username, role: user.role } };
const payload = authUser(user);
const token = app.jwt.sign(payload);
return { token, user: payload };
});
app.get("/api/auth/me", { preHandler: [requireAuth] }, async (request) => {

View File

@@ -1,6 +1,7 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import dotenv from "dotenv";
import type { UserRole } from "./types.js";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const serverRoot = path.resolve(__dirname, "..");
@@ -8,10 +9,19 @@ const serverRoot = path.resolve(__dirname, "..");
dotenv.config({ path: path.resolve(serverRoot, "..", ".env") });
dotenv.config({ path: path.resolve(serverRoot, ".env") });
function fixedLoginRole(): UserRole {
return process.env.FIXED_LOGIN_ROLE === "admin" ? "admin" : "user";
}
export const config = {
host: process.env.HOST ?? "0.0.0.0",
port: Number(process.env.PORT ?? 3001),
jwtSecret: process.env.JWT_SECRET ?? "dev-only-change-me",
authMode: process.env.APP_AUTH_MODE === "fixed" ? "fixed" : "login",
fixedLogin: {
username: process.env.FIXED_LOGIN_USERNAME ?? "viewer",
role: fixedLoginRole()
},
dataDir: path.resolve(serverRoot, "data"),
storageDir: path.resolve(serverRoot, "storage"),
databasePath: process.env.DB_PATH ?? path.resolve(serverRoot, "data", "model-library.db"),