M2-T11: serve React SPA from FastAPI and remove Jinja pages

- app/main.py serves the SPA build (SPA_DIST_DIR, default frontend/dist):
  mounts /assets and a GET catch-all returning index.html for client routes;
  catch-all 404s on /api/*, never swallows /docs, /openapi.json, /static, assets,
  ingestion/ticktick/status; skips SPA serving when dist absent (backend-only CI)
- delete app/api/routes/pages.py, app/api/routes/auth.py, app/templates/
  (all replaced by /api/* + SPA; auth service layer kept)
- remove/replace Jinja page tests (JSON coverage already in test_api_*);
  add tests/test_spa_hosting.py for the fallback contract
- regenerate openapi/ (Jinja paths gone) and frontend schema.d.ts
This commit is contained in:
2026-06-13 11:29:14 +02:00
parent 8aa7316b26
commit a9830c42d8
18 changed files with 319 additions and 2094 deletions
-365
View File
@@ -21,127 +21,6 @@ export interface paths {
patch?: never;
trace?: never;
};
"/login": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Login Page */
get: operations["login_page_login_get"];
put?: never;
/** Login Submit */
post: operations["login_submit_login_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/config/change-password": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/** Change Password Submit */
post: operations["change_password_submit_config_change_password_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/logout": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/** Logout */
post: operations["logout_logout_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Home */
get: operations["home__get"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/admin": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Admin Redirect */
get: operations["admin_redirect_admin_get"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/config": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Config Page */
get: operations["config_page_config_get"];
put?: never;
/** Config Submit */
post: operations["config_submit_config_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/config/smtp/test": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/** Smtp Test Submit */
post: operations["smtp_test_submit_config_smtp_test_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/config": {
parameters: {
query?: never;
@@ -544,31 +423,6 @@ export interface paths {
export type webhooks = Record<string, never>;
export interface components {
schemas: {
/** Body_change_password_submit_config_change_password_post */
Body_change_password_submit_config_change_password_post: {
/** Current Password */
current_password: string;
/** New Password */
new_password: string;
/** Confirm Password */
confirm_password: string;
/** Csrf Token */
csrf_token: string;
};
/** Body_login_submit_login_post */
Body_login_submit_login_post: {
/** Username */
username: string;
/** Password */
password: string;
/** Csrf Token */
csrf_token: string;
};
/** Body_logout_logout_post */
Body_logout_logout_post: {
/** Csrf Token */
csrf_token: string;
};
/** ConfigField */
ConfigField: {
/** Env Name */
@@ -831,225 +685,6 @@ export interface operations {
};
};
};
login_page_login_get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
};
};
login_submit_login_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/x-www-form-urlencoded": components["schemas"]["Body_login_submit_login_post"];
};
};
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
change_password_submit_config_change_password_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/x-www-form-urlencoded": components["schemas"]["Body_change_password_submit_config_change_password_post"];
};
};
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
logout_logout_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/x-www-form-urlencoded": components["schemas"]["Body_logout_logout_post"];
};
};
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": unknown;
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
home__get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
};
};
admin_redirect_admin_get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
};
};
config_page_config_get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
};
};
config_submit_config_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
};
};
smtp_test_submit_config_smtp_test_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"text/html": string;
};
};
};
};
get_config_api_config_get: {
parameters: {
query?: never;