feat: enhance API and middleware functionality: add public API routes, implement program and course member checks, and improve rate limiting for invite acceptance. Update OpenAPI spec generation to focus on public API endpoints and add new middleware for automation key scopes. Include new documentation for lesson AI features and public API tests.
This commit is contained in:
@@ -40,6 +40,7 @@ export class BaseApi {
|
||||
|
||||
this.isLoading = true;
|
||||
this.error = null;
|
||||
this.success = false;
|
||||
|
||||
try {
|
||||
const response = await requestFn();
|
||||
@@ -50,6 +51,7 @@ export class BaseApi {
|
||||
|
||||
if (apiResult.success === false) {
|
||||
console.log('apiResult false', apiResult);
|
||||
this.error = JSON.stringify(apiResult);
|
||||
options.onError?.(apiResult as ExtractError<TrpcType>);
|
||||
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/** Backend error JSON from API `handleError` responses (also carried as thrown `ApiError.message` on 4xx). */
|
||||
export type ApiErrorBody = {
|
||||
success: false;
|
||||
error: string;
|
||||
code: string;
|
||||
field?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses `this.error` / ApiError.message when the API returned a JSON body with `success: false`.
|
||||
*/
|
||||
export function tryParseApiErrorBody(raw: string | null | undefined): ApiErrorBody | null {
|
||||
if (!raw?.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (
|
||||
typeof parsed === 'object' &&
|
||||
parsed !== null &&
|
||||
'success' in parsed &&
|
||||
(parsed as { success: unknown }).success === false &&
|
||||
'code' in parsed &&
|
||||
typeof (parsed as { code: unknown }).code === 'string' &&
|
||||
'error' in parsed &&
|
||||
typeof (parsed as { error: unknown }).error === 'string'
|
||||
) {
|
||||
return parsed as ApiErrorBody;
|
||||
}
|
||||
} catch {
|
||||
// not JSON
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,4 +1,19 @@
|
||||
import { env } from '$env/dynamic/private';
|
||||
import { ApiError } from './types';
|
||||
|
||||
export type ServerApiResult<T> = { ok: true; status: number; body: T } | { ok: false; status: number; message: string };
|
||||
|
||||
function getServerApiErrorMessage(error: unknown): string {
|
||||
if (error instanceof ApiError) {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
return 'Unknown server API error';
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side helper to get API key headers for RPC client calls
|
||||
@@ -26,3 +41,51 @@ export function getApiKeyHeaders(): { headers: { Authorization: string } } {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap server-side API calls so SSR loaders can degrade gracefully instead of throwing.
|
||||
*/
|
||||
export async function safeServerApi<T>(requestFn: () => Promise<Response>): Promise<ServerApiResult<T>> {
|
||||
try {
|
||||
const response = await requestFn();
|
||||
const result = (await response.json()) as unknown;
|
||||
|
||||
if (typeof result === 'object' && result !== null && 'success' in result) {
|
||||
if ((result as { success: boolean }).success === true) {
|
||||
return {
|
||||
ok: true,
|
||||
status: response.status,
|
||||
body: result as T
|
||||
};
|
||||
}
|
||||
|
||||
const errorResult = result as { error?: string; message?: string } | null;
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
status: response.status,
|
||||
message: errorResult?.error ?? errorResult?.message ?? response.statusText
|
||||
};
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return {
|
||||
ok: true,
|
||||
status: response.status,
|
||||
body: result as T
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
status: response.status,
|
||||
message: response.statusText || 'Unexpected server API response'
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
ok: false,
|
||||
status: error instanceof ApiError ? (error.status ?? 0) : 0,
|
||||
message: getServerApiErrorMessage(error)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user