Skip to content

feat: migrate to OpenAPI-typed API client#13

Draft
sjfhsjfh wants to merge 1 commit into
mainfrom
openapi
Draft

feat: migrate to OpenAPI-typed API client#13
sjfhsjfh wants to merge 1 commit into
mainfrom
openapi

Conversation

@sjfhsjfh

Copy link
Copy Markdown
Contributor

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.

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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ts typed client with bearer injection and 401 refresh + replay behavior; login now stores both access + refresh tokens.
  • Replaced fetchApi(...) usage across views/components with api.GET/POST/PATCH(...) calls and updated src/model.ts types to alias OpenAPI schema types.
  • Generated and committed src/api/schema.d.ts, plus tooling (pnpm gen:api) and env typing for VITE_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 thread src/common.ts
Comment on lines 97 to 99
const cookieListener: (() => void)[] = [];

function triggerCookie() {
Comment thread src/common.ts
Comment on lines 129 to 132
export function pleaseLogin(router: Router) {
router.push('/login');
toast(i18n.global.t('please-login'), 'error');
}
Comment thread src/api/client.ts
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 thread src/views/OAuthView.vue
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 thread src/views/StaffView.vue
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 thread src/views/ChartsView.vue
Comment on lines +307 to +311
const query: Record<string, unknown> = {
pageNum: PAGE_NUM,
page: Number(parameters.value.page),
order: parameters.value.order,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants