""" Routines API - Brilli-style routine management Routines have ordered steps. Users start sessions to walk through them. """ import os import flask import jwt import core.auth as auth import core.postgres as postgres def _get_user_uuid(token): try: payload = jwt.decode(token, os.getenv("JWT_SECRET"), algorithms=["HS256"]) return payload.get("sub") except (jwt.ExpiredSignatureError, jwt.InvalidTokenError): return None def _auth(request): """Extract and verify token. Returns user_uuid or None.""" header = request.headers.get("Authorization", "") if not header.startswith("Bearer "): return None token = header[7:] user_uuid = _get_user_uuid(token) if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid): return None return user_uuid def register(app): # ── Routines CRUD ───────────────────────────────────────────── @app.route("/api/routines", methods=["GET"]) def api_listRoutines(): """List all routines for the logged-in user.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass @app.route("/api/routines", methods=["POST"]) def api_createRoutine(): """Create a new routine. Body: {name, description?, icon?}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass @app.route("/api/routines/", methods=["GET"]) def api_getRoutine(routine_id): """Get a routine with its steps.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass @app.route("/api/routines/", methods=["PUT"]) def api_updateRoutine(routine_id): """Update routine details. Body: {name?, description?, icon?}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass @app.route("/api/routines/", methods=["DELETE"]) def api_deleteRoutine(routine_id): """Delete a routine and all its steps/sessions.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass # ── Steps CRUD ──────────────────────────────────────────────── @app.route("/api/routines//steps", methods=["GET"]) def api_listSteps(routine_id): """List steps for a routine, ordered by position.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass @app.route("/api/routines//steps", methods=["POST"]) def api_addStep(routine_id): """Add a step to a routine. Body: {name, duration_minutes?, position?}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass @app.route("/api/routines//steps/", methods=["PUT"]) def api_updateStep(routine_id, step_id): """Update a step. Body: {name?, duration_minutes?, position?}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass @app.route("/api/routines//steps/", methods=["DELETE"]) def api_deleteStep(routine_id, step_id): """Delete a step from a routine.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass @app.route("/api/routines//steps/reorder", methods=["PUT"]) def api_reorderSteps(routine_id): """Reorder steps. Body: {step_ids: [ordered list of step UUIDs]}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass # ── Routine Sessions (active run-through) ───────────────────── @app.route("/api/routines//start", methods=["POST"]) def api_startRoutine(routine_id): """Start a routine session. Returns the session with first step.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass @app.route("/api/sessions/active", methods=["GET"]) def api_getActiveSession(): """Get the user's currently active routine session, if any.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass @app.route("/api/sessions//complete-step", methods=["POST"]) def api_completeStep(session_id): """Mark current step done, advance to next. Body: {step_id}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass @app.route("/api/sessions//skip-step", methods=["POST"]) def api_skipStep(session_id): """Skip current step, advance to next. Body: {step_id}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass @app.route("/api/sessions//cancel", methods=["POST"]) def api_cancelSession(session_id): """Cancel an active routine session.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass # ── Routine History / Stats ─────────────────────────────────── @app.route("/api/routines//history", methods=["GET"]) def api_routineHistory(routine_id): """Get past sessions for a routine. Query: ?days=7""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass # ── Routine Scheduling ──────────────────────────────────────── @app.route("/api/routines//schedule", methods=["PUT"]) def api_setRoutineSchedule(routine_id): """Set when this routine should run. Body: {days: ["mon","tue",...], time: "08:00", remind: true}""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() pass @app.route("/api/routines//schedule", methods=["GET"]) def api_getRoutineSchedule(routine_id): """Get the schedule for a routine.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass @app.route("/api/routines//schedule", methods=["DELETE"]) def api_deleteRoutineSchedule(routine_id): """Remove the schedule from a routine.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 pass