이메일 인증과 비밀번호 재설정 추가

This commit is contained in:
2026-06-25 12:02:44 +09:00
parent edf48e53a5
commit 35a2976182
26 changed files with 770 additions and 43 deletions

View File

@@ -64,6 +64,20 @@ export function initDb(dbPath) {
);
CREATE INDEX IF NOT EXISTS idx_deck_favorites_share_code ON deck_favorites(share_code);
CREATE TABLE IF NOT EXISTS auth_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
type TEXT NOT NULL,
token_hash TEXT NOT NULL UNIQUE,
expires_at TEXT NOT NULL,
used_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_auth_tokens_user_type
ON auth_tokens(user_id, type);
`);
const deckCols = db.prepare('PRAGMA table_info(decks)').all();
@@ -81,6 +95,32 @@ export function initDb(dbPath) {
if (!userCols.some((c) => c.name === 'last_login_at')) {
db.exec('ALTER TABLE users ADD COLUMN last_login_at TEXT');
}
if (!userCols.some((c) => c.name === 'email_verified_at')) {
db.exec('ALTER TABLE users ADD COLUMN email_verified_at TEXT');
db.exec(`
UPDATE users
SET email_verified_at = COALESCE(updated_at, created_at, datetime('now'))
WHERE email_verified_at IS NULL
`);
}
const usernameCaseConflicts = db.prepare(`
SELECT lower(username) AS normalized_username, COUNT(*) AS count
FROM users
GROUP BY lower(username)
HAVING COUNT(*) > 1
LIMIT 1
`).get();
if (!usernameCaseConflicts) {
db.exec(`
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_username_nocase
ON users(username COLLATE NOCASE)
`);
} else {
console.warn(
'대소문자만 다른 기존 사용자명이 있어 NOCASE 고유 인덱스를 만들지 못했습니다. 관리자 확인이 필요합니다.',
);
}
return db;
}
@@ -103,7 +143,10 @@ export function seedAdmin(db) {
const existingUser = db.prepare('SELECT id FROM users WHERE email = ?').get(email);
if (existingUser) {
db.prepare(`
UPDATE users SET role = 'admin', updated_at = datetime('now')
UPDATE users
SET role = 'admin',
email_verified_at = COALESCE(email_verified_at, datetime('now')),
updated_at = datetime('now')
WHERE id = ?
`).run(existingUser.id);
return;
@@ -111,7 +154,9 @@ export function seedAdmin(db) {
const hash = bcrypt.hashSync(password, 12);
db.prepare(`
INSERT INTO users (email, username, password_hash, display_name, role)
VALUES (?, ?, ?, ?, 'admin')
INSERT INTO users (
email, username, password_hash, display_name, role, email_verified_at
)
VALUES (?, ?, ?, ?, 'admin', datetime('now'))
`).run(email, username, hash, '관리자');
}