Full backend service with: - FastAPI REST API with CRUD, search, reprocess endpoints - PostgreSQL + pgvector for items and semantic search - Redis + RQ for background job processing - Meilisearch for fast keyword/filter search - Browserless/Chrome for JS rendering and screenshots - OpenAI structured output for AI classification - Local file storage with S3-ready abstraction - Gateway auth via X-Gateway-User-Id header - Own docker-compose stack (6 containers) Classification: fixed folders (Home/Family/Work/Travel/Knowledge/Faith/Projects) and fixed tags (28 predefined). AI assigns exactly 1 folder, 2-3 tags, title, summary, and confidence score per item. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91 lines
2.7 KiB
Python
91 lines
2.7 KiB
Python
"""
|
|
Platform Gateway — Session and service-connection helpers.
|
|
"""
|
|
|
|
import secrets
|
|
from datetime import datetime, timedelta
|
|
import bcrypt
|
|
|
|
from config import SESSION_MAX_AGE, DEV_AUTO_LOGIN_USERNAME, DEV_AUTO_LOGIN_DISPLAY_NAME
|
|
from database import get_db
|
|
|
|
|
|
def create_session(user_id):
|
|
token = secrets.token_hex(32)
|
|
expires = (datetime.now() + timedelta(seconds=SESSION_MAX_AGE)).isoformat()
|
|
conn = get_db()
|
|
conn.execute("INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)",
|
|
(token, user_id, expires))
|
|
conn.commit()
|
|
conn.close()
|
|
return token
|
|
|
|
|
|
def get_session_user(token):
|
|
if not token:
|
|
return None
|
|
conn = get_db()
|
|
row = conn.execute("""
|
|
SELECT u.* FROM sessions s
|
|
JOIN users u ON s.user_id = u.id
|
|
WHERE s.token = ? AND s.expires_at > ?
|
|
""", (token, datetime.now().isoformat())).fetchone()
|
|
conn.close()
|
|
return dict(row) if row else None
|
|
|
|
|
|
def delete_session(token):
|
|
if not token:
|
|
return
|
|
conn = get_db()
|
|
conn.execute("DELETE FROM sessions WHERE token = ?", (token,))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
|
|
def get_service_token(user_id, service):
|
|
conn = get_db()
|
|
row = conn.execute(
|
|
"SELECT auth_type, auth_token FROM service_connections WHERE user_id = ? AND service = ?",
|
|
(user_id, service)
|
|
).fetchone()
|
|
conn.close()
|
|
return dict(row) if row else None
|
|
|
|
|
|
def get_or_create_dev_user():
|
|
conn = get_db()
|
|
row = conn.execute("SELECT * FROM users WHERE username = ?", (DEV_AUTO_LOGIN_USERNAME,)).fetchone()
|
|
if row:
|
|
conn.close()
|
|
return dict(row)
|
|
|
|
pw_hash = bcrypt.hashpw(secrets.token_hex(16).encode(), bcrypt.gensalt()).decode()
|
|
conn.execute(
|
|
"INSERT INTO users (username, password_hash, display_name) VALUES (?, ?, ?)",
|
|
(DEV_AUTO_LOGIN_USERNAME, pw_hash, DEV_AUTO_LOGIN_DISPLAY_NAME)
|
|
)
|
|
conn.commit()
|
|
row = conn.execute("SELECT * FROM users WHERE username = ?", (DEV_AUTO_LOGIN_USERNAME,)).fetchone()
|
|
conn.close()
|
|
return dict(row) if row else None
|
|
|
|
|
|
def set_service_token(user_id, service, auth_token, auth_type="bearer"):
|
|
conn = get_db()
|
|
conn.execute("""
|
|
INSERT INTO service_connections (user_id, service, auth_type, auth_token)
|
|
VALUES (?, ?, ?, ?)
|
|
ON CONFLICT(user_id, service) DO UPDATE SET auth_token = ?, auth_type = ?
|
|
""", (user_id, service, auth_type, auth_token, auth_token, auth_type))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
|
|
def delete_service_token(user_id, service):
|
|
conn = get_db()
|
|
conn.execute("DELETE FROM service_connections WHERE user_id = ? AND service = ?",
|
|
(user_id, service))
|
|
conn.commit()
|
|
conn.close()
|