Files
Synculous-2/api/main.py
chelsea 97a166f5aa Fix medication system and rename to Synculous.
- Add all 14 missing database tables (medications, med_logs, routines, etc.)
- Rewrite medication scheduling: support specific days, every N days, as-needed (PRN)
- Fix taken_times matching: match by created_at date, not scheduled_time string
- Fix adherence calculation: taken / expected doses, not taken / (taken + skipped)
- Add formatSchedule() helper for readable display
- Update client types and API layer
- Rename brilli-ins-client → synculous-client
- Make client PWA: add manifest, service worker, icons
- Bind dev server to 0.0.0.0 for network access
- Fix SVG icon bugs in Icons.tsx
- Add .dockerignore for client npm caching

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 03:23:38 -06:00

155 lines
5.4 KiB
Python

"""
main.py - Flask API with auth routes and module registry
Domain routes are registered via the routes registry.
"""
import os
import flask
from flask_cors import CORS
import core.auth as auth
import core.users as users
import core.postgres as postgres
import api.routes.routines as routines_routes
import api.routes.medications as medications_routes
import api.routes.routine_steps_extended as routine_steps_extended_routes
import api.routes.routine_sessions_extended as routine_sessions_extended_routes
import api.routes.routine_templates as routine_templates_routes
import api.routes.routine_stats as routine_stats_routes
import api.routes.routine_tags as routine_tags_routes
app = flask.Flask(__name__)
CORS(app)
ROUTE_MODULES = [
routines_routes,
medications_routes,
routine_steps_extended_routes,
routine_sessions_extended_routes,
routine_templates_routes,
routine_stats_routes,
routine_tags_routes,
]
def register_routes(module):
"""Register a routes module. Module should have a register(app) function."""
ROUTE_MODULES.append(module)
# ── Auth Routes ────────────────────────────────────────────────────
@app.route("/api/register", methods=["POST"])
def api_register():
data = flask.request.get_json()
username = data.get("username")
password = data.get("password")
if not username or not password:
return flask.jsonify({"error": "username and password required"}), 400
result = users.registerUser(username, password, data)
if result:
return flask.jsonify({"success": True}), 201
else:
return flask.jsonify({"error": "username taken"}), 409
@app.route("/api/login", methods=["POST"])
def api_login():
data = flask.request.get_json()
username = data.get("username")
password = data.get("password")
if not username or not password:
return flask.jsonify({"error": "username and password required"}), 400
token = auth.getLoginToken(username, password)
if token:
return flask.jsonify({"token": token}), 200
else:
return flask.jsonify({"error": "invalid credentials"}), 401
# ── User Routes ────────────────────────────────────────────────────
@app.route("/api/getUserUUID/<username>", methods=["GET"])
def api_getUserUUID(username):
header = flask.request.headers.get("Authorization", "")
if not header.startswith("Bearer "):
return flask.jsonify({"error": "missing token"}), 401
token = header[7:]
if auth.verifyLoginToken(token, username):
return flask.jsonify(users.getUserUUID(username)), 200
else:
return flask.jsonify({"error": "unauthorized"}), 401
@app.route("/api/user/<userUUID>", methods=["GET"])
def api_getUser(userUUID):
header = flask.request.headers.get("Authorization", "")
if not header.startswith("Bearer "):
return flask.jsonify({"error": "missing token"}), 401
token = header[7:]
if auth.verifyLoginToken(token, userUUID=userUUID):
user = postgres.select_one("users", {"id": userUUID})
if user:
user.pop("password_hashed", None)
return flask.jsonify(user), 200
else:
return flask.jsonify({"error": "user not found"}), 404
else:
return flask.jsonify({"error": "unauthorized"}), 401
@app.route("/api/user/<userUUID>", methods=["PUT"])
def api_updateUser(userUUID):
header = flask.request.headers.get("Authorization", "")
if not header.startswith("Bearer "):
return flask.jsonify({"error": "missing token"}), 401
token = header[7:]
if auth.verifyLoginToken(token, userUUID=userUUID):
data = flask.request.get_json()
result = users.updateUser(userUUID, data)
if result:
return flask.jsonify({"success": True}), 200
else:
return flask.jsonify({"error": "no valid fields to update"}), 400
else:
return flask.jsonify({"error": "unauthorized"}), 401
@app.route("/api/user/<userUUID>", methods=["DELETE"])
def api_deleteUser(userUUID):
header = flask.request.headers.get("Authorization", "")
if not header.startswith("Bearer "):
return flask.jsonify({"error": "missing token"}), 401
token = header[7:]
if auth.verifyLoginToken(token, userUUID=userUUID):
data = flask.request.get_json()
password = data.get("password")
if not password:
return flask.jsonify(
{"error": "password required for account deletion"}
), 400
result = auth.unregisterUser(userUUID, password)
if result:
return flask.jsonify({"success": True}), 200
else:
return flask.jsonify({"error": "invalid password"}), 401
else:
return flask.jsonify({"error": "unauthorized"}), 401
# ── Health Check ───────────────────────────────────────────────────
@app.route("/health", methods=["GET"])
def health_check():
return flask.jsonify({"status": "ok"}), 200
if __name__ == "__main__":
for module in ROUTE_MODULES:
if hasattr(module, "register"):
module.register(app)
app.run(host="0.0.0.0", port=5000)