feat: multi-user auth — local accounts, settings page, OAuth prep
FlowDeck CI / test (push) Failing after 6s
FlowDeck CI / docker (push) Has been skipped

Phase 1 foundation:
- DB: users table extended (password_hash, is_active, login_attempts, locked_until)
- DB: user_oauth_tokens table (user_id, provider, access_token, refresh_token)
- password_utils.py: SHA-256+salt hashing, verify, rate-limit lock check
- auth.py: POST /auth/register + POST /auth/local-login + /auth/login?provider=local
- Login page: tabs Login/Register with Gitea OAuth button
- settings.html: profile (name/password), forges, API tokens, sessions
- ALTER TABLE migrations for existing DBs
This commit is contained in:
2026-07-10 15:34:58 -04:00
parent 246aef65ac
commit 32c1f70c53
4 changed files with 410 additions and 3 deletions
+25 -1
View File
@@ -27,7 +27,12 @@ def init_db():
full_name TEXT NOT NULL DEFAULT '',
email TEXT NOT NULL DEFAULT '',
avatar_url TEXT NOT NULL DEFAULT '',
is_admin BOOLEAN NOT NULL DEFAULT 0,
password_hash TEXT,
is_admin INTEGER NOT NULL DEFAULT 0,
is_active INTEGER NOT NULL DEFAULT 1,
last_login TIMESTAMP,
login_attempts INTEGER NOT NULL DEFAULT 0,
locked_until TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
@@ -39,6 +44,19 @@ def init_db():
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS user_oauth_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
provider TEXT NOT NULL,
access_token TEXT NOT NULL,
refresh_token TEXT,
expires_at TIMESTAMP,
instance_url TEXT DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, provider)
);
CREATE TABLE IF NOT EXISTS boards (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_owner TEXT NOT NULL,
@@ -304,6 +322,12 @@ def init_db():
conn.execute("ALTER TABLE pages ADD COLUMN published INTEGER DEFAULT 0")
except sqlite3.OperationalError:
pass
# v2.2: Auth locale
for col in ["password_hash", "is_active", "last_login", "login_attempts", "locked_until"]:
try:
conn.execute(f"ALTER TABLE users ADD COLUMN {col} {'TEXT' if col in ('password_hash','last_login','locked_until') else 'INTEGER NOT NULL DEFAULT ' + ('1' if col=='is_active' else '0')}")
except sqlite3.OperationalError:
pass
conn.commit()