Conversation
Adopt openapi-typescript + openapi-fetch as the single source of truth for the API layer. schema.d.ts is generated from phira-api's openapi.json (`pnpm gen:api`); model.ts data types now alias the generated schema types (only the runtime Permission/Role bitmask logic stays hand-written). A typed client (src/api/client.ts) replaces the hand-rolled useFetchApi: onRequest injects the bearer from the access_token cookie, onResponse handles 401 by de-duplicated refresh_token rotation + replay (falling back to logout/redirect), login now stores both access and refresh tokens. All ~50 fetchApi call sites migrated to api.GET/POST/PATCH with typed paths/queries/bodies. Removes useFetchApi/uploadFile/FetchApi dead code. Fixes surfaced by schema alignment: UserView no longer wrongly inherits email (only /me sends it), UserAvatar accepts nullable avatar, StbStatus v-for keyed by index (history items have no id), plus corrected nullability for std/std_score/description/likes via the schema aliases. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates the frontend API layer to an OpenAPI-generated type system (openapi-typescript) and a typed HTTP client (openapi-fetch), replacing the previous hand-rolled useFetchApi and aligning domain types with the backend schema.
Changes:
- Added
src/api/client.tstyped client with bearer injection and 401 refresh + replay behavior; login now stores both access + refresh tokens. - Replaced
fetchApi(...)usage across views/components withapi.GET/POST/PATCH(...)calls and updatedsrc/model.tstypes to alias OpenAPI schema types. - Generated and committed
src/api/schema.d.ts, plus tooling (pnpm gen:api) and env typing forVITE_API_HOST.
Reviewed changes
Copilot reviewed 28 out of 31 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/api/client.ts | New typed OpenAPI client with auth header injection and refresh-on-401 replay logic |
| src/api/schema.d.ts | Generated OpenAPI typings used as the API single source of truth |
| src/model.ts | Domain types now alias OpenAPI schema types; keeps runtime Permission/Role logic |
| src/common.ts | Removes legacy fetch helpers; cookie helpers remain central for token storage |
| src/views/*.vue | Migrates view-level API calls to typed client and adjusts request/response handling |
| src/components/*.vue | Migrates component-level API calls to typed client and updates schema-aligned typing |
| src/settings/*.vue | Migrates settings API calls; updates avatar upload to use new upload endpoint |
| package.json / env.d.ts | Adds OpenAPI tooling/deps and environment typing |
| pnpm-workspace.yaml | Workspace build configuration for esbuild |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
97
to
99
| const cookieListener: (() => void)[] = []; | ||
|
|
||
| function triggerCookie() { |
Comment on lines
129
to
132
| export function pleaseLogin(router: Router) { | ||
| router.push('/login'); | ||
| toast(i18n.global.t('please-login'), 'error'); | ||
| } |
Comment on lines
+90
to
+93
| // Re-issue the original request with the freshly-stored access token. | ||
| const headers = new Headers(request.headers); | ||
| headers.set('Authorization', `Bearer ${getCookie('access_token')}`); | ||
| return options.fetch(new Request(request, { headers })); |
Comment on lines
+92
to
+96
| const upRes = await api.POST('/upload/{name}', { | ||
| params: { path: { name: 'avatar' } }, | ||
| body: avatarFile.value as unknown as number[], | ||
| }); | ||
| if (!upRes.data) throw new Error('error'); |
Comment on lines
+47
to
57
| const { data, error } = await api.GET('/oauth/authorize', { | ||
| params: { | ||
| query: { | ||
| response_type: 'code', | ||
| client_id: clientID, | ||
| redirect_uri: redirectURI.toString(), | ||
| scope, | ||
| state, | ||
| }, | ||
| }, | ||
| }); |
Comment on lines
+54
to
+61
| const resp = await api.GET('/user', { | ||
| params: { | ||
| query: { | ||
| pageNum: PAGE_NUM, | ||
| ...(parameters.value as object), | ||
| } as UserListQuery, | ||
| }, | ||
| }); |
Comment on lines
+21
to
+23
| const { data: staffData, error: staffErr } = await api.GET('/staff'); | ||
| if (staffErr || !staffData) throw new Error(); | ||
| let staff = ref<Roles>(staffData); |
Comment on lines
+156
to
160
| const query: Record<string, unknown> = { | ||
| pageNum: PAGE_NUM, | ||
| page: Number(parameters.value.page), | ||
| order: parameters.value.order, | ||
| }; |
Comment on lines
+307
to
+311
| const query: Record<string, unknown> = { | ||
| pageNum: PAGE_NUM, | ||
| page: Number(parameters.value.page), | ||
| order: parameters.value.order, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adopt openapi-typescript + openapi-fetch as the single source of truth for the API layer. schema.d.ts is generated from phira-api's openapi.json (
pnpm gen:api); model.ts data types now alias the generated schema types (only the runtime Permission/Role bitmask logic stays hand-written).A typed client (src/api/client.ts) replaces the hand-rolled useFetchApi: onRequest injects the bearer from the access_token cookie, onResponse handles 401 by de-duplicated refresh_token rotation + replay (falling back to logout/redirect), login now stores both access and refresh tokens.
All ~50 fetchApi call sites migrated to api.GET/POST/PATCH with typed paths/queries/bodies. Removes useFetchApi/uploadFile/FetchApi dead code.
Fixes surfaced by schema alignment: UserView no longer wrongly inherits email (only /me sends it), UserAvatar accepts nullable avatar, StbStatus v-for keyed by index (history items have no id), plus corrected nullability for std/std_score/description/likes via the schema aliases.