|
|
|
@@ -16,7 +16,7 @@ from fastapi import APIRouter, Header, HTTPException, Request
|
|
|
|
|
from fastapi.responses import JSONResponse, Response
|
|
|
|
|
|
|
|
|
|
from app.db import get_conn
|
|
|
|
|
from app.services.api_v2_helpers import (
|
|
|
|
|
from app.services.api_v2_helpers import ( # noqa: F401 — require_scope est utilisé par les handlers
|
|
|
|
|
audit_log,
|
|
|
|
|
check_idempotency,
|
|
|
|
|
check_v2_rate_limit,
|
|
|
|
@@ -24,6 +24,7 @@ from app.services.api_v2_helpers import (
|
|
|
|
|
has_scope,
|
|
|
|
|
paginate_headers,
|
|
|
|
|
parse_pagination,
|
|
|
|
|
require_scope,
|
|
|
|
|
row_to_dict,
|
|
|
|
|
store_idempotency,
|
|
|
|
|
to_iso8601,
|
|
|
|
@@ -47,10 +48,8 @@ def _v2_rate_check(request: Request, user: dict) -> None:
|
|
|
|
|
|
|
|
|
|
@router.post("/tokens")
|
|
|
|
|
async def create_token(request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -110,10 +109,8 @@ async def list_tokens(request: Request, authorization: str | None = Header(defau
|
|
|
|
|
|
|
|
|
|
@router.delete("/tokens/{token_id}")
|
|
|
|
|
async def revoke_token(token_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT id, user_id FROM api_tokens WHERE id=?", (token_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -127,10 +124,8 @@ async def revoke_token(token_id: int, request: Request, authorization: str | Non
|
|
|
|
|
|
|
|
|
|
@router.post("/tokens/{token_id}/rotate")
|
|
|
|
|
async def rotate_token(token_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT id, user_id, name, scopes FROM api_tokens WHERE id=?", (token_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -171,10 +166,8 @@ async def get_me(request: Request, authorization: str | None = Header(default=No
|
|
|
|
|
|
|
|
|
|
@router.patch("/users/me")
|
|
|
|
|
async def patch_me(request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -239,10 +232,8 @@ async def list_workspaces(request: Request, authorization: str | None = Header(d
|
|
|
|
|
|
|
|
|
|
@router.post("/workspaces")
|
|
|
|
|
async def create_workspace(request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
idem = check_idempotency(request, user["id"])
|
|
|
|
|
if idem:
|
|
|
|
|
return JSONResponse(content=idem["data"], status_code=idem["status"])
|
|
|
|
@@ -291,10 +282,8 @@ async def get_workspace(workspace_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.patch("/workspaces/{workspace_id}")
|
|
|
|
|
async def patch_workspace(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -321,10 +310,8 @@ async def patch_workspace(workspace_id: int, request: Request, authorization: st
|
|
|
|
|
|
|
|
|
|
@router.delete("/workspaces/{workspace_id}")
|
|
|
|
|
async def delete_workspace(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT * FROM workspaces WHERE id=?", (workspace_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -349,10 +336,8 @@ async def list_workspace_members(workspace_id: int, request: Request, authorizat
|
|
|
|
|
|
|
|
|
|
@router.post("/workspaces/{workspace_id}/members")
|
|
|
|
|
async def invite_member(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -389,10 +374,8 @@ async def invite_member(workspace_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.patch("/workspaces/{workspace_id}/members/{uid}")
|
|
|
|
|
async def update_member_role(workspace_id: int, uid: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -417,10 +400,8 @@ async def update_member_role(workspace_id: int, uid: int, request: Request, auth
|
|
|
|
|
|
|
|
|
|
@router.delete("/workspaces/{workspace_id}/members/{uid}")
|
|
|
|
|
async def remove_member(workspace_id: int, uid: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
ws = conn.execute("SELECT owner_id FROM workspaces WHERE id=?", (workspace_id,)).fetchone()
|
|
|
|
|
if not ws:
|
|
|
|
@@ -470,10 +451,8 @@ async def list_collections_v2(request: Request, authorization: str | None = Head
|
|
|
|
|
|
|
|
|
|
@router.post("/collections")
|
|
|
|
|
async def create_collection_v2(request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
idem = check_idempotency(request, user["id"])
|
|
|
|
|
if idem:
|
|
|
|
|
return JSONResponse(content=idem["data"], status_code=idem["status"])
|
|
|
|
@@ -528,10 +507,8 @@ async def get_collection_v2(collection_id: int, request: Request, authorization:
|
|
|
|
|
|
|
|
|
|
@router.patch("/collections/{collection_id}")
|
|
|
|
|
async def patch_collection_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -555,10 +532,8 @@ async def patch_collection_v2(collection_id: int, request: Request, authorizatio
|
|
|
|
|
|
|
|
|
|
@router.delete("/collections/{collection_id}")
|
|
|
|
|
async def delete_collection_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT * FROM collections WHERE id=?", (collection_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -570,10 +545,8 @@ async def delete_collection_v2(collection_id: int, request: Request, authorizati
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/linked")
|
|
|
|
|
async def create_linked_db(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -609,10 +582,8 @@ async def create_linked_db(collection_id: int, request: Request, authorization:
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/task")
|
|
|
|
|
async def toggle_task(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT is_task FROM collections WHERE id=?", (collection_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -636,10 +607,8 @@ async def list_sources(collection_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/sources")
|
|
|
|
|
async def add_source(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -662,10 +631,8 @@ async def add_source(collection_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.delete("/collections/{collection_id}/sources/{source_id}")
|
|
|
|
|
async def remove_source(collection_id: int, source_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM collection_data_sources WHERE collection_id=? AND (id=? OR source_collection_id=?)", (collection_id, source_id, source_id))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -734,10 +701,8 @@ async def list_collection_pages_v2(collection_id: int, request: Request, authori
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/pages")
|
|
|
|
|
async def create_collection_page_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
idem = check_idempotency(request, user["id"])
|
|
|
|
|
if idem:
|
|
|
|
|
return JSONResponse(content=idem["data"], status_code=idem["status"])
|
|
|
|
@@ -816,10 +781,8 @@ async def get_page_v2(page_id: int, request: Request, authorization: str | None
|
|
|
|
|
|
|
|
|
|
@router.patch("/pages/{page_id}")
|
|
|
|
|
async def patch_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -865,10 +828,8 @@ async def patch_page_v2(page_id: int, request: Request, authorization: str | Non
|
|
|
|
|
|
|
|
|
|
@router.delete("/pages/{page_id}")
|
|
|
|
|
async def delete_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT * FROM collection_pages WHERE id=?", (page_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -884,10 +845,8 @@ async def delete_page_v2(page_id: int, request: Request, authorization: str | No
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/restore")
|
|
|
|
|
async def restore_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
# For soft-deleted pages (deleted_at) - but collection_pages has no deleted_at; handle pages table
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT deleted_at FROM pages WHERE id=?", (page_id,)).fetchone()
|
|
|
|
@@ -903,10 +862,8 @@ async def restore_page_v2(page_id: int, request: Request, authorization: str | N
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/move")
|
|
|
|
|
async def move_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -934,10 +891,8 @@ async def list_sub_items_v2(page_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/sub-items")
|
|
|
|
|
async def create_sub_item_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -965,10 +920,8 @@ async def list_dependencies_v2(page_id: int, request: Request, authorization: st
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/dependencies")
|
|
|
|
|
async def add_dependency_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -992,10 +945,8 @@ async def add_dependency_v2(page_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.delete("/pages/{page_id}/dependencies/{dep_id}")
|
|
|
|
|
async def remove_dependency_v2(page_id: int, dep_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM page_dependencies WHERE page_id=? AND dependency_id=?", (page_id, dep_id))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1016,10 +967,8 @@ async def list_properties_v2(collection_id: int, request: Request, authorization
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/properties")
|
|
|
|
|
async def create_property_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1043,10 +992,8 @@ async def create_property_v2(collection_id: int, request: Request, authorization
|
|
|
|
|
|
|
|
|
|
@router.patch("/properties/{prop_id}")
|
|
|
|
|
async def patch_property_v2(prop_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1069,10 +1016,8 @@ async def patch_property_v2(prop_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.delete("/properties/{prop_id}")
|
|
|
|
|
async def delete_property_v2(prop_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
if not conn.execute("SELECT id FROM collection_properties WHERE id=?", (prop_id,)).fetchone():
|
|
|
|
|
raise HTTPException(404, "Property not found")
|
|
|
|
@@ -1083,10 +1028,8 @@ async def delete_property_v2(prop_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.post("/properties/{prop_id}/relation")
|
|
|
|
|
async def create_relation_v2(prop_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1158,10 +1101,8 @@ async def list_views_v2(collection_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/views")
|
|
|
|
|
async def create_view_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1185,10 +1126,8 @@ async def create_view_v2(collection_id: int, request: Request, authorization: st
|
|
|
|
|
|
|
|
|
|
@router.patch("/views/{view_id}")
|
|
|
|
|
async def patch_view_v2(view_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1218,10 +1157,8 @@ async def patch_view_v2(view_id: int, request: Request, authorization: str | Non
|
|
|
|
|
|
|
|
|
|
@router.delete("/views/{view_id}")
|
|
|
|
|
async def delete_view_v2(view_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
if not conn.execute("SELECT id FROM collection_views WHERE id=?", (view_id,)).fetchone():
|
|
|
|
|
raise HTTPException(404, "View not found")
|
|
|
|
@@ -1232,10 +1169,8 @@ async def delete_view_v2(view_id: int, request: Request, authorization: str | No
|
|
|
|
|
|
|
|
|
|
@router.post("/views/{view_id}/save-as")
|
|
|
|
|
async def save_as_view_v2(view_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1261,10 +1196,8 @@ async def list_dashboards_v2(collection_id: int, request: Request, authorization
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/dashboards")
|
|
|
|
|
async def create_dashboard_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1279,10 +1212,8 @@ async def create_dashboard_v2(collection_id: int, request: Request, authorizatio
|
|
|
|
|
|
|
|
|
|
@router.patch("/dashboards/{dashboard_id}")
|
|
|
|
|
async def patch_dashboard_v2(dashboard_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1303,10 +1234,8 @@ async def patch_dashboard_v2(dashboard_id: int, request: Request, authorization:
|
|
|
|
|
|
|
|
|
|
@router.delete("/dashboards/{dashboard_id}")
|
|
|
|
|
async def delete_dashboard_v2(dashboard_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM collection_dashboards WHERE id=?", (dashboard_id,))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1326,10 +1255,8 @@ async def list_comments_v2(page_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/comments")
|
|
|
|
|
async def create_comment_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1351,10 +1278,8 @@ async def create_comment_v2(page_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.patch("/comments/{comment_id}")
|
|
|
|
|
async def patch_comment_v2(comment_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1379,10 +1304,8 @@ async def patch_comment_v2(comment_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.delete("/comments/{comment_id}")
|
|
|
|
|
async def delete_comment_v2(comment_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT * FROM comments WHERE id=?", (comment_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -1395,10 +1318,8 @@ async def delete_comment_v2(comment_id: int, request: Request, authorization: st
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/mentions")
|
|
|
|
|
async def create_mention_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1498,10 +1419,8 @@ async def list_favorites_v2(request: Request, authorization: str | None = Header
|
|
|
|
|
|
|
|
|
|
@router.post("/favorites")
|
|
|
|
|
async def add_favorite_v2(request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1523,10 +1442,8 @@ async def add_favorite_v2(request: Request, authorization: str | None = Header(d
|
|
|
|
|
|
|
|
|
|
@router.delete("/favorites/{page_id}")
|
|
|
|
|
async def remove_favorite_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM favorites WHERE user_id=? AND page_id=?", (user["id"], page_id))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1550,10 +1467,8 @@ async def list_tags_v2(request: Request, authorization: str | None = Header(defa
|
|
|
|
|
|
|
|
|
|
@router.post("/tags")
|
|
|
|
|
async def create_tag_v2(request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1573,10 +1488,8 @@ async def create_tag_v2(request: Request, authorization: str | None = Header(def
|
|
|
|
|
|
|
|
|
|
@router.patch("/tags/{tag_id}")
|
|
|
|
|
async def patch_tag_v2(tag_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1593,10 +1506,8 @@ async def patch_tag_v2(tag_id: int, request: Request, authorization: str | None
|
|
|
|
|
|
|
|
|
|
@router.delete("/tags/{tag_id}")
|
|
|
|
|
async def delete_tag_v2(tag_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM tags WHERE id=? AND user_id=?", (tag_id, user["id"]))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1604,10 +1515,8 @@ async def delete_tag_v2(tag_id: int, request: Request, authorization: str | None
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/tags")
|
|
|
|
|
async def attach_tag_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1625,10 +1534,8 @@ async def attach_tag_v2(page_id: int, request: Request, authorization: str | Non
|
|
|
|
|
|
|
|
|
|
@router.delete("/pages/{page_id}/tags/{tag_id}")
|
|
|
|
|
async def detach_tag_v2(page_id: int, tag_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM page_tags WHERE page_id=? AND tag_id=?", (page_id, tag_id))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1659,10 +1566,8 @@ async def list_shares_v2(page_id: int, request: Request, authorization: str | No
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/shares")
|
|
|
|
|
async def create_share_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1688,10 +1593,8 @@ async def create_share_v2(page_id: int, request: Request, authorization: str | N
|
|
|
|
|
|
|
|
|
|
@router.patch("/shares/{share_id}")
|
|
|
|
|
async def patch_share_v2(share_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1708,10 +1611,8 @@ async def patch_share_v2(share_id: int, request: Request, authorization: str | N
|
|
|
|
|
|
|
|
|
|
@router.delete("/shares/{share_id}")
|
|
|
|
|
async def delete_share_v2(share_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT page_id FROM page_shares WHERE id=?", (share_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -1726,10 +1627,8 @@ async def delete_share_v2(share_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/publish")
|
|
|
|
|
async def publish_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1749,10 +1648,8 @@ async def publish_page_v2(page_id: int, request: Request, authorization: str | N
|
|
|
|
|
|
|
|
|
|
@router.delete("/pages/{page_id}/publish")
|
|
|
|
|
async def unpublish_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("UPDATE pages SET is_published=0 WHERE id=?", (page_id,))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1776,10 +1673,8 @@ async def list_history_v2(page_id: int, request: Request, authorization: str | N
|
|
|
|
|
|
|
|
|
|
@router.post("/pages/{page_id}/history/restore")
|
|
|
|
|
async def restore_history_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1820,10 +1715,8 @@ async def list_sprints_v2(collection_id: int, request: Request, authorization: s
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/sprints")
|
|
|
|
|
async def create_sprint_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1845,10 +1738,8 @@ async def create_sprint_v2(collection_id: int, request: Request, authorization:
|
|
|
|
|
|
|
|
|
|
@router.patch("/sprints/{sprint_id}")
|
|
|
|
|
async def patch_sprint_v2(sprint_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1872,10 +1763,8 @@ async def patch_sprint_v2(sprint_id: int, request: Request, authorization: str |
|
|
|
|
|
|
|
|
|
|
@router.delete("/sprints/{sprint_id}")
|
|
|
|
|
async def delete_sprint_v2(sprint_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM sprints WHERE id=?", (sprint_id,))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1883,10 +1772,8 @@ async def delete_sprint_v2(sprint_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.post("/sprints/{sprint_id}/assign")
|
|
|
|
|
async def assign_sprint_v2(sprint_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1904,10 +1791,8 @@ async def assign_sprint_v2(sprint_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.delete("/sprints/{sprint_id}/assign/{page_id}")
|
|
|
|
|
async def unassign_sprint_v2(sprint_id: int, page_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM sprint_pages WHERE sprint_id=? AND page_id=?", (sprint_id, page_id))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -1951,10 +1836,8 @@ async def list_templates_v2(collection_id: int, request: Request, authorization:
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/templates")
|
|
|
|
|
async def create_template_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1970,10 +1853,8 @@ async def create_template_v2(collection_id: int, request: Request, authorization
|
|
|
|
|
|
|
|
|
|
@router.patch("/templates/{template_id}")
|
|
|
|
|
async def patch_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -1991,10 +1872,8 @@ async def patch_template_v2(template_id: int, request: Request, authorization: s
|
|
|
|
|
|
|
|
|
|
@router.delete("/templates/{template_id}")
|
|
|
|
|
async def delete_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM page_templates WHERE id=?", (template_id,))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -2002,10 +1881,8 @@ async def delete_template_v2(template_id: int, request: Request, authorization:
|
|
|
|
|
|
|
|
|
|
@router.post("/templates/{template_id}/apply")
|
|
|
|
|
async def apply_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
tpl = conn.execute("SELECT * FROM page_templates WHERE id=?", (template_id,)).fetchone()
|
|
|
|
|
if not tpl:
|
|
|
|
@@ -2027,10 +1904,8 @@ async def list_db_templates_v2(request: Request, authorization: str | None = Hea
|
|
|
|
|
|
|
|
|
|
@router.post("/templates/database/{template_id}/apply")
|
|
|
|
|
async def apply_db_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -2104,10 +1979,8 @@ async def export_csv_v2(collection_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.post("/collections/{collection_id}/import/csv")
|
|
|
|
|
async def import_csv_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
form = await request.form()
|
|
|
|
|
file = form.get("file")
|
|
|
|
@@ -2283,10 +2156,8 @@ async def list_webhooks_v2(request: Request, authorization: str | None = Header(
|
|
|
|
|
|
|
|
|
|
@router.post("/webhooks")
|
|
|
|
|
async def create_webhook_v2(request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -2313,10 +2184,8 @@ async def create_webhook_v2(request: Request, authorization: str | None = Header
|
|
|
|
|
|
|
|
|
|
@router.patch("/webhooks/{webhook_id}")
|
|
|
|
|
async def patch_webhook_v2(webhook_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
try:
|
|
|
|
|
body = await request.json()
|
|
|
|
|
except Exception:
|
|
|
|
@@ -2336,10 +2205,8 @@ async def patch_webhook_v2(webhook_id: int, request: Request, authorization: str
|
|
|
|
|
|
|
|
|
|
@router.delete("/webhooks/{webhook_id}")
|
|
|
|
|
async def delete_webhook_v2(webhook_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
conn.execute("DELETE FROM webhook_subscriptions WHERE id=?", (webhook_id,))
|
|
|
|
|
conn.commit()
|
|
|
|
@@ -2348,10 +2215,8 @@ async def delete_webhook_v2(webhook_id: int, request: Request, authorization: st
|
|
|
|
|
|
|
|
|
|
@router.post("/webhooks/{webhook_id}/test")
|
|
|
|
|
async def test_webhook_v2(webhook_id: int, request: Request, authorization: str | None = Header(default=None)):
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
with get_conn() as conn:
|
|
|
|
|
row = conn.execute("SELECT * FROM webhook_subscriptions WHERE id=?", (webhook_id,)).fetchone()
|
|
|
|
|
if not row:
|
|
|
|
@@ -2399,10 +2264,8 @@ async def list_deliveries_v2(webhook_id: int, request: Request,
|
|
|
|
|
async def retry_webhook_deliveries(webhook_id: int, request: Request,
|
|
|
|
|
authorization: str | None = Header(default=None)):
|
|
|
|
|
"""Manually retry failed deliveries for a webhook."""
|
|
|
|
|
user = get_bearer_user(request, authorization)
|
|
|
|
|
user = require_scope("write")(request, authorization)
|
|
|
|
|
_v2_rate_check(request, user)
|
|
|
|
|
if not has_scope(user.get("_token_scopes"), "write"):
|
|
|
|
|
raise HTTPException(403, "Insufficient scope. Required: write")
|
|
|
|
|
|
|
|
|
|
from app.services.webhook_outbound import retry_due_deliveries
|
|
|
|
|
|
|
|
|
|