Per-user auth via Nextcloud Login Flow v2, Tailwind UI rewrite
- auth.py/auth_routes.py: Login Flow v2 (verified against real NC source/docs) - no OAuth2 client registration needed, backend polls server-side so no CORS issues. Sessions are hashed-token cookies; NC app passwords encrypted at rest (Fernet). Every /api/* route guarded by a blueprint-wide before_request, not per-route decorators, so future routes are protected by default. - receipts/groups scoped per owner_nc_user_id; cross-user access 404s. - nc_client/cospend_client take (username, app_password) per call instead of one shared global credential - each user's uploads/shares/ bills now happen as themselves. - Frontend: LoginGate component drives the login flow (open NC login in a new tab, poll our backend, done). - Merged the old separate review step into the split screen, redesigned with Tailwind (was unstyled/broken), default-excluded-per-item splitting with one-click "include everyone" fixed, DD.MM.YYYY date field, receipt-icon branding. - Dropped LLM bounding-box highlighting - unreliable on real receipts, plain photo upload instead. - Only mention who a bill is split with in its title when there's more than one bill off the same receipt to disambiguate.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Cospend's authenticated (NC-login) API - same app-password auth as
|
||||
nc_client. This is an OCS API (same family as the Share API), confirmed
|
||||
against the real routes/controller source in julien-nc/cospend-nc:
|
||||
"""Cospend's authenticated (NC-login) API - same per-user app-password auth
|
||||
as nc_client (see auth.py). This is an OCS API (same family as the Share
|
||||
API), confirmed against the real routes/controller source in
|
||||
julien-nc/cospend-nc:
|
||||
|
||||
appinfo/routes.php ('ocs' section):
|
||||
GET /api/{v}/projects -> api#getLocalProjects
|
||||
@@ -28,10 +29,6 @@ from .config import Config
|
||||
_HEADERS = {"OCS-APIRequest": "true"}
|
||||
|
||||
|
||||
def _auth() -> tuple[str, str]:
|
||||
return (Config.NC_USERNAME, Config.NC_APP_PASSWORD)
|
||||
|
||||
|
||||
def _base(project_id: str | None = None) -> str:
|
||||
root = f"{Config.NC_BASE_URL}/ocs/v2.php/apps/cospend/api/v1"
|
||||
if project_id is None:
|
||||
@@ -39,11 +36,11 @@ def _base(project_id: str | None = None) -> str:
|
||||
return f"{root}/projects/{project_id}"
|
||||
|
||||
|
||||
def get_projects() -> list[dict[str, Any]]:
|
||||
def get_projects(username: str, app_password: str) -> list[dict[str, Any]]:
|
||||
"""Lists Cospend projects visible to the authenticated user."""
|
||||
resp = requests.get(
|
||||
f"{_base()}/projects",
|
||||
auth=_auth(),
|
||||
auth=(username, app_password),
|
||||
headers=_HEADERS,
|
||||
params={"format": "json"},
|
||||
timeout=15,
|
||||
@@ -52,10 +49,10 @@ def get_projects() -> list[dict[str, Any]]:
|
||||
return resp.json()["ocs"]["data"]
|
||||
|
||||
|
||||
def get_members(project_id: str) -> list[dict[str, Any]]:
|
||||
def get_members(username: str, app_password: str, project_id: str) -> list[dict[str, Any]]:
|
||||
resp = requests.get(
|
||||
f"{_base(project_id)}/members",
|
||||
auth=_auth(),
|
||||
auth=(username, app_password),
|
||||
headers=_HEADERS,
|
||||
params={"format": "json"},
|
||||
timeout=15,
|
||||
@@ -65,6 +62,8 @@ def get_members(project_id: str) -> list[dict[str, Any]]:
|
||||
|
||||
|
||||
def create_bill(
|
||||
username: str,
|
||||
app_password: str,
|
||||
*,
|
||||
project_id: str,
|
||||
what: str,
|
||||
@@ -82,7 +81,7 @@ def create_bill(
|
||||
"""
|
||||
resp = requests.post(
|
||||
f"{_base(project_id)}/bills",
|
||||
auth=_auth(),
|
||||
auth=(username, app_password),
|
||||
headers=_HEADERS,
|
||||
params={"format": "json"},
|
||||
json={
|
||||
@@ -92,6 +91,9 @@ def create_bill(
|
||||
"payedFor": ",".join(str(i) for i in ower_ids),
|
||||
"comment": comment,
|
||||
"date": date, # YYYY-MM-DD, the receipt's issue date, not today
|
||||
# required by LocalProjectService::createBill (400s without it);
|
||||
# 'n' = FREQUENCY_NO, i.e. this bill doesn't repeat.
|
||||
"repeat": "n",
|
||||
},
|
||||
timeout=15,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user