feat: Sprint 2-3 — Auth JWT + Multi-tenant (HRT-28)
- auth_db.py: create users, subscriptions, refresh_tokens tables in turf_saas.db - auth.py: register/login/refresh/logout endpoints, JWT middleware, plan_required decorator, free daily-limit check - middleware.py: in-memory rate limiter (100 req/min/IP), timestamped access logs - saas_api.py: Flask app factory wiring JWT, CORS, blueprints, /api/v1/predictions plan-gating - tests/test_auth.py: 27 pytest tests, 83% coverage (target >=80%) - API_AUTH.md: full endpoint documentation Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
68
auth_db.py
Normal file
68
auth_db.py
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Auth DB — users and subscriptions schema for turf_saas.db
|
||||
Sprint 2-3: Auth JWT + Multi-tenant (HRT-28)
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
DB_PATH = os.environ.get("TURF_SAAS_DB", "/home/h3r7/turf_saas/turf_saas.db")
|
||||
|
||||
|
||||
def get_db():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
|
||||
def init_auth_tables():
|
||||
"""Create users and subscriptions tables if they don't exist."""
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
|
||||
c.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
plan TEXT NOT NULL DEFAULT 'free'
|
||||
CHECK(plan IN ('free','premium','pro')),
|
||||
created_at DATETIME NOT NULL DEFAULT (datetime('now')),
|
||||
is_active INTEGER NOT NULL DEFAULT 1,
|
||||
daily_usage INTEGER NOT NULL DEFAULT 0,
|
||||
last_usage_date TEXT DEFAULT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS subscriptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
plan TEXT NOT NULL CHECK(plan IN ('free','premium','pro')),
|
||||
start_date DATETIME NOT NULL DEFAULT (datetime('now')),
|
||||
end_date DATETIME,
|
||||
stripe_customer_id TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
created_at DATETIME NOT NULL DEFAULT (datetime('now')),
|
||||
expires_at DATETIME NOT NULL,
|
||||
revoked INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_subscriptions_user ON subscriptions(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user ON refresh_tokens(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_hash ON refresh_tokens(token_hash);
|
||||
""")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print("[auth_db] Tables users, subscriptions, refresh_tokens created/verified.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_auth_tables()
|
||||
Reference in New Issue
Block a user