feat: 管理员账号体系 + 动态邀请码管理; 各端内置数据固定 uuid 对齐
This commit is contained in:
@@ -4,14 +4,28 @@ import { getDb } from './db.mjs';
|
||||
|
||||
export function register({ username, password, device_name, invite_code }) {
|
||||
const d = getDb();
|
||||
// 多用户管控:邀请码(设置后必填)+ 用户数上限(0=不限)
|
||||
const invite = process.env.INVITE_CODE;
|
||||
if (invite && invite_code !== invite) throw new Error('邀请码无效');
|
||||
// 多用户管控:管理员颁发邀请码;env INVITE_CODE 作为静态兜底
|
||||
const maxUsers = Number(process.env.MAX_USERS || 0);
|
||||
if (maxUsers > 0) {
|
||||
const cnt = d.prepare('SELECT COUNT(*) AS c FROM users').get().c;
|
||||
if (cnt >= maxUsers) throw new Error('注册名额已满,请联系管理员');
|
||||
}
|
||||
const userCount = d.prepare('SELECT COUNT(*) AS c FROM users').get().c;
|
||||
let isAdmin = 0;
|
||||
if (userCount === 0) {
|
||||
isAdmin = 1; // 首位注册者自动成为管理员
|
||||
} else {
|
||||
const code = String(invite_code || '').trim().toUpperCase();
|
||||
if (code) {
|
||||
const row = d.prepare('SELECT id, valid FROM invite_codes WHERE code=?').get(code);
|
||||
if (!row || !row.valid) throw new Error('邀请码无效或已使用');
|
||||
var __usedInviteId = row.id; // eslint-disable-line no-var
|
||||
} else if (process.env.INVITE_CODE && invite_code === process.env.INVITE_CODE) {
|
||||
// 静态兜底码
|
||||
} else {
|
||||
throw new Error('需要邀请码,请联系管理员');
|
||||
}
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_-]{3,32}$/.test(username || '')) throw new Error('用户名需为 3-32 位字母数字-_');
|
||||
if (!password || password.length < 8) throw new Error('密码至少 8 位');
|
||||
const exists = d.prepare('SELECT 1 FROM users WHERE username=?').get(username);
|
||||
@@ -20,8 +34,12 @@ export function register({ username, password, device_name, invite_code }) {
|
||||
const salt = crypto.randomBytes(16).toString('hex');
|
||||
const hash = crypto.scryptSync(password, salt, 64).toString('hex');
|
||||
const user_id = crypto.randomUUID();
|
||||
d.prepare('INSERT INTO users (id, username, password_hash, salt) VALUES (?,?,?,?)')
|
||||
.run(user_id, username, hash, salt);
|
||||
d.prepare('INSERT INTO users (id, username, password_hash, salt, is_admin) VALUES (?,?,?,?,?)')
|
||||
.run(user_id, username, hash, salt, isAdmin);
|
||||
if (typeof __usedInviteId === 'number') {
|
||||
d.prepare("UPDATE invite_codes SET used_by=?, used_at=datetime('now','localtime'), valid=0 WHERE id=?")
|
||||
.run(user_id, __usedInviteId);
|
||||
}
|
||||
return issueToken(user_id, device_name);
|
||||
}
|
||||
|
||||
@@ -56,6 +74,12 @@ function issueToken(user_id, device_name) {
|
||||
return { token, user_id, device_id, device_name: name, expires_at: expires };
|
||||
}
|
||||
|
||||
export function isUserAdmin(user_id) {
|
||||
const d = getDb();
|
||||
const row = d.prepare('SELECT is_admin FROM users WHERE id=?').get(user_id);
|
||||
return !!row && !!row.is_admin;
|
||||
}
|
||||
|
||||
export function authUser(req) {
|
||||
const d = getDb();
|
||||
const header = req.headers['authorization'] || '';
|
||||
|
||||
Reference in New Issue
Block a user