first commit
This commit is contained in:
204
api/routes/routines.py
Normal file
204
api/routes/routines.py
Normal file
@@ -0,0 +1,204 @@
|
||||
"""
|
||||
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/<routine_id>", 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/<routine_id>", 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/<routine_id>", 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/<routine_id>/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/<routine_id>/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/<routine_id>/steps/<step_id>", 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/<routine_id>/steps/<step_id>", 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/<routine_id>/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/<routine_id>/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/<session_id>/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/<session_id>/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/<session_id>/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/<routine_id>/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/<routine_id>/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/<routine_id>/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/<routine_id>/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
|
||||
Reference in New Issue
Block a user