From f0b4a38775f32e17f14dd83603dad99debba8e06 Mon Sep 17 00:00:00 2001 From: Jake Walker Date: Wed, 5 Feb 2025 23:29:48 +0000 Subject: [PATCH] fix spa routing --- Taskfile.yml | 2 ++ .../src/client/@tanstack/solid-query.gen.ts | 23 ++++++++++++++-- client/src/client/sdk.gen.ts | 14 ++++++++-- client/src/client/types.gen.ts | 27 ++++++++++++++++++- src/main.py | 15 ++++++----- 5 files changed, 70 insertions(+), 11 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 5ab5ee0..494cbe1 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -41,3 +41,5 @@ tasks: deps: [frontend:generate] cmds: - pnpm run build + - rm -rf ../dist + - mv dist ../dist diff --git a/client/src/client/@tanstack/solid-query.gen.ts b/client/src/client/@tanstack/solid-query.gen.ts index d7830bc..811d1f6 100644 --- a/client/src/client/@tanstack/solid-query.gen.ts +++ b/client/src/client/@tanstack/solid-query.gen.ts @@ -2,8 +2,8 @@ import type { Options } from '@hey-api/client-fetch'; import { queryOptions, type MutationOptions } from '@tanstack/solid-query'; -import type { GetAppConfigData, ReadUserData, ReadPodcastsData, CreatePodcastData, CreatePodcastError, CreatePodcastResponse, DeletePodcastData, DeletePodcastError, ReadPodcastData, UpdatePodcastData, UpdatePodcastError, UpdatePodcastResponse, UpdatePodcastImageData, UpdatePodcastImageError, UpdatePodcastImageResponse, ReadEpisodesData, AdminUploadEpisodeData, AdminUploadEpisodeError, DeleteEpisodeData, DeleteEpisodeError, ReadEpisodeData, UpdateEpisodeData, UpdateEpisodeError, UpdateEpisodeResponse, EpisodeAdditionalUploadData, EpisodeAdditionalUploadError, EpisodeAdditionalUploadResponse, GetPodcastFeedData, GetEpisodeOrCoverData } from '../types.gen'; -import { getAppConfig, readUser, readPodcasts, createPodcast, deletePodcast, readPodcast, updatePodcast, updatePodcastImage, readEpisodes, adminUploadEpisode, deleteEpisode, readEpisode, updateEpisode, episodeAdditionalUpload, getPodcastFeed, getEpisodeOrCover, client } from '../sdk.gen'; +import type { GetAppConfigData, ReadUserData, ReadPodcastsData, CreatePodcastData, CreatePodcastError, CreatePodcastResponse, DeletePodcastData, DeletePodcastError, ReadPodcastData, UpdatePodcastData, UpdatePodcastError, UpdatePodcastResponse, UpdatePodcastImageData, UpdatePodcastImageError, UpdatePodcastImageResponse, ReadEpisodesData, AdminUploadEpisodeData, AdminUploadEpisodeError, DeleteEpisodeData, DeleteEpisodeError, ReadEpisodeData, UpdateEpisodeData, UpdateEpisodeError, UpdateEpisodeResponse, EpisodeAdditionalUploadData, EpisodeAdditionalUploadError, EpisodeAdditionalUploadResponse, GetPodcastFeedData, GetEpisodeOrCoverData, ServeAppData } from '../types.gen'; +import { getAppConfig, readUser, readPodcasts, createPodcast, deletePodcast, readPodcast, updatePodcast, updatePodcastImage, readEpisodes, adminUploadEpisode, deleteEpisode, readEpisode, updateEpisode, episodeAdditionalUpload, getPodcastFeed, getEpisodeOrCover, serveApp, client } from '../sdk.gen'; type QueryKey = [ Pick & { @@ -370,4 +370,23 @@ export const getEpisodeOrCoverOptions = (options: Options }, queryKey: getEpisodeOrCoverQueryKey(options) }); +}; + +export const serveAppQueryKey = (options: Options) => [ + createQueryKey('serveApp', options) +]; + +export const serveAppOptions = (options: Options) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await serveApp({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: serveAppQueryKey(options) + }); }; \ No newline at end of file diff --git a/client/src/client/sdk.gen.ts b/client/src/client/sdk.gen.ts index e79cf16..479390d 100644 --- a/client/src/client/sdk.gen.ts +++ b/client/src/client/sdk.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts import { createClient, createConfig, type Options, formDataBodySerializer } from '@hey-api/client-fetch'; -import type { GetAppConfigData, GetAppConfigResponse, ReadUserData, ReadUserResponse, ReadPodcastsData, ReadPodcastsResponse, CreatePodcastData, CreatePodcastResponse, CreatePodcastError, DeletePodcastData, DeletePodcastError, ReadPodcastData, ReadPodcastResponse, ReadPodcastError, UpdatePodcastData, UpdatePodcastResponse, UpdatePodcastError, UpdatePodcastImageData, UpdatePodcastImageResponse, UpdatePodcastImageError, ReadEpisodesData, ReadEpisodesResponse, ReadEpisodesError, AdminUploadEpisodeData, AdminUploadEpisodeError, DeleteEpisodeData, DeleteEpisodeError, ReadEpisodeData, ReadEpisodeResponse, ReadEpisodeError, UpdateEpisodeData, UpdateEpisodeResponse, UpdateEpisodeError, EpisodeAdditionalUploadData, EpisodeAdditionalUploadResponse, EpisodeAdditionalUploadError, GetPodcastFeedData, GetPodcastFeedError, GetEpisodeOrCoverData, GetEpisodeOrCoverError } from './types.gen'; +import type { GetAppConfigData, GetAppConfigResponse, ReadUserData, ReadUserResponse, ReadPodcastsData, ReadPodcastsResponse, CreatePodcastData, CreatePodcastResponse, CreatePodcastError, DeletePodcastData, DeletePodcastError, ReadPodcastData, ReadPodcastResponse, ReadPodcastError, UpdatePodcastData, UpdatePodcastResponse, UpdatePodcastError, UpdatePodcastImageData, UpdatePodcastImageResponse, UpdatePodcastImageError, ReadEpisodesData, ReadEpisodesResponse, ReadEpisodesError, AdminUploadEpisodeData, AdminUploadEpisodeError, DeleteEpisodeData, DeleteEpisodeError, ReadEpisodeData, ReadEpisodeResponse, ReadEpisodeError, UpdateEpisodeData, UpdateEpisodeResponse, UpdateEpisodeError, EpisodeAdditionalUploadData, EpisodeAdditionalUploadResponse, EpisodeAdditionalUploadError, GetPodcastFeedData, GetPodcastFeedError, GetEpisodeOrCoverData, GetEpisodeOrCoverError, ServeAppData, ServeAppError } from './types.gen'; export const client = createClient(createConfig()); @@ -187,7 +187,17 @@ export const getPodcastFeed = (options: Op */ export const getEpisodeOrCover = (options: Options) => { return (options?.client ?? client).get({ - url: '/{podcast_id}/{filename}', + url: '/files/{podcast_id}/{filename}', + ...options + }); +}; + +/** + * Serve App + */ +export const serveApp = (options: Options) => { + return (options?.client ?? client).get({ + url: '/{full_path}', ...options }); }; \ No newline at end of file diff --git a/client/src/client/types.gen.ts b/client/src/client/types.gen.ts index 3d27488..8135257 100644 --- a/client/src/client/types.gen.ts +++ b/client/src/client/types.gen.ts @@ -444,7 +444,7 @@ export type GetEpisodeOrCoverData = { filename: string; }; query?: never; - url: '/{podcast_id}/{filename}'; + url: '/files/{podcast_id}/{filename}'; }; export type GetEpisodeOrCoverErrors = { @@ -461,4 +461,29 @@ export type GetEpisodeOrCoverResponses = { * Successful Response */ 200: unknown; +}; + +export type ServeAppData = { + body?: never; + path: { + full_path: string; + }; + query?: never; + url: '/{full_path}'; +}; + +export type ServeAppErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ServeAppError = ServeAppErrors[keyof ServeAppErrors]; + +export type ServeAppResponses = { + /** + * Successful Response + */ + 200: unknown; }; \ No newline at end of file diff --git a/src/main.py b/src/main.py index 3f4cc57..aa5fe05 100644 --- a/src/main.py +++ b/src/main.py @@ -429,7 +429,7 @@ def get_podcast_feed(session: SessionDep, request: Request, podcast_id: str): description=podcast.description, image=urllib.parse.urljoin( str(request.base_url), - f"/{podcast.id}/{podcast.image_filename}", + f"/files/{podcast.id}/{podcast.image_filename}", ) if podcast.image_filename is not None else None, @@ -446,7 +446,7 @@ def get_podcast_feed(session: SessionDep, request: Request, podcast_id: str): publication_date=episode.publish_date.astimezone(tz=timezone.utc), media=podgen.Media( urllib.parse.urljoin( - str(request.base_url), f"{podcast.id}/{episode.id}.m4a" + str(request.base_url), f"/files/{podcast.id}/{episode.id}.m4a" ), episode.file_size, duration=timedelta(seconds=episode.duration) @@ -460,7 +460,7 @@ def get_podcast_feed(session: SessionDep, request: Request, podcast_id: str): return Response(content=feed.rss_str(), media_type="application/xml") -@app.get("/{podcast_id}/{filename}") +@app.get("/files/{podcast_id}/{filename}") def get_episode_or_cover(session: SessionDep, podcast_id: str, filename: str): podcast = session.exec( select(models.Podcast).where(models.Podcast.id == podcast_id) @@ -492,9 +492,12 @@ def get_episode_or_cover(session: SessionDep, podcast_id: str, filename: str): return HTTPException(status_code=404, detail="File not found") -@app.get("/{catchall:path}") -async def serve_app(catchall: str): - return FileResponse("dist/index.html") +@app.get("/{full_path:path}") +async def serve_app(full_path: str): + if not full_path.startswith("api/"): + return FileResponse("dist/index.html") + + return HTTPException(status_code=404, detail="Not found") use_route_names_as_operation_ids(app)