Skip to content

Commit 06110cf

Browse files
authored
Merge pull request #895 from objectstack-ai/copilot/fix-auth-endpoint-paths
2 parents 562c2f5 + 1494d6a commit 06110cf

2 files changed

Lines changed: 31 additions & 14 deletions

File tree

ROADMAP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ business/custom objects, aligning with industry best practices (e.g., ServiceNow
331331
- v3.x: The `@objectstack/plugin-auth` ObjectQL adapter now includes `AUTH_MODEL_TO_PROTOCOL` mapping to translate better-auth's hardcoded model names (`user`, `session`, `account`, `verification`) to protocol names (`sys_user`, `sys_session`, `sys_account`, `sys_verification`). Custom adapters must adopt the same mapping.
332332
- v3.x: **Bug fix**`AuthManager.createDatabaseConfig()` now wraps the ObjectQL adapter as a `DBAdapterInstance` factory function (`(options) => DBAdapter`). Previously the raw adapter object was passed, which fell through to the Kysely adapter path and failed silently. `AuthManager.handleRequest()` and `AuthPlugin.registerAuthRoutes()` now inspect `response.status >= 500` and log the error body, since better-auth catches internal errors and returns 500 Responses without throwing.
333333
- v3.x: **Bug fix**`AuthPlugin` now defers HTTP route registration to a `kernel:ready` hook instead of doing it synchronously in `start()`. This makes the plugin resilient to plugin loading order — the `http-server` service is guaranteed to be available after all plugins complete their init/start phases. The CLI `serve` command also registers `HonoServerPlugin` before config plugins (with duplicate detection) for the same reason.
334+
- v3.x: **Bug fix** — Studio `useApiDiscovery` hook no longer hardcodes auth endpoints as `/api/auth/...`. The `discover()` callback now fetches `/api/v1/discovery` and reads `routes.auth` to dynamically construct auth endpoint paths (falling back to `/api/v1/auth`). The session endpoint is corrected from `/session` to `/get-session` to align with better-auth's `AuthEndpointPaths.getSession`.
334335
- v4.0: Legacy un-prefixed aliases will be fully removed.
335336

336337
---

apps/studio/src/hooks/use-api-discovery.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,15 @@ const SYSTEM_ENDPOINTS: EndpointDef[] = [
3333
{ method: 'GET', path: '/api/v1/health', desc: 'Health check', group: 'System' },
3434
];
3535

36-
const AUTH_ENDPOINTS: EndpointDef[] = [
37-
{ method: 'POST', path: '/api/auth/sign-in/email', desc: 'Sign in (email)', group: 'Auth', bodyTemplate: { email: 'user@example.com', password: '' } },
38-
{ method: 'POST', path: '/api/auth/sign-up/email', desc: 'Sign up (email)', group: 'Auth', bodyTemplate: { email: '', password: '', name: '' } },
39-
{ method: 'POST', path: '/api/auth/sign-out', desc: 'Sign out', group: 'Auth' },
40-
{ method: 'GET', path: '/api/auth/session', desc: 'Get session', group: 'Auth' },
41-
];
36+
/** Build auth endpoints from the discovered auth base path. */
37+
function buildAuthEndpoints(authBase: string): EndpointDef[] {
38+
return [
39+
{ method: 'POST', path: `${authBase}/sign-in/email`, desc: 'Sign in (email)', group: 'Auth', bodyTemplate: { email: 'user@example.com', password: '' } },
40+
{ method: 'POST', path: `${authBase}/sign-up/email`, desc: 'Sign up (email)', group: 'Auth', bodyTemplate: { email: '', password: '', name: '' } },
41+
{ method: 'POST', path: `${authBase}/sign-out`, desc: 'Sign out', group: 'Auth' },
42+
{ method: 'GET', path: `${authBase}/get-session`, desc: 'Get session', group: 'Auth' },
43+
];
44+
}
4245

4346
// ─── Hook ───────────────────────────────────────────────────────────
4447

@@ -54,7 +57,20 @@ export function useApiDiscovery() {
5457
setError(null);
5558

5659
try {
57-
// 1. Fetch metadata types
60+
// 1. Resolve auth base path from discovery
61+
let authBase = '/api/v1/auth';
62+
try {
63+
const discRes = await fetch('/api/v1/discovery');
64+
if (discRes.ok) {
65+
const discData = await discRes.json();
66+
const routes = discData?.data?.routes ?? discData?.routes;
67+
if (routes?.auth) authBase = routes.auth;
68+
}
69+
} catch {
70+
// Keep default /api/v1/auth
71+
}
72+
73+
// 2. Fetch metadata types
5874
let metaTypes: string[] = [];
5975
try {
6076
const typesResult = await client.meta.getTypes();
@@ -67,7 +83,7 @@ export function useApiDiscovery() {
6783
// Meta types may not be available
6884
}
6985

70-
// 2. Fetch object names from metadata
86+
// 3. Fetch object names from metadata
7187
let objectNames: string[] = [];
7288
try {
7389
const objectType = metaTypes.includes('objects') ? 'objects' : metaTypes.includes('object') ? 'object' : null;
@@ -83,7 +99,7 @@ export function useApiDiscovery() {
8399
// Objects may not be available
84100
}
85101

86-
// 3. Build dynamic data endpoints for each object
102+
// 4. Build dynamic data endpoints for each object
87103
const dataEndpoints: EndpointDef[] = objectNames.flatMap(name => [
88104
{ method: 'GET' as HttpMethod, path: `/api/v1/data/${name}`, desc: `List ${name}`, group: `Data: ${name}` },
89105
{ method: 'POST' as HttpMethod, path: `/api/v1/data/${name}`, desc: `Create ${name}`, group: `Data: ${name}`, bodyTemplate: { name: 'example' } },
@@ -92,7 +108,7 @@ export function useApiDiscovery() {
92108
{ method: 'DELETE' as HttpMethod, path: `/api/v1/data/${name}/:id`, desc: `Delete ${name}`, group: `Data: ${name}` },
93109
]);
94110

95-
// 4. Build metadata endpoints for each type
111+
// 5. Build metadata endpoints for each type
96112
const metaEndpoints: EndpointDef[] = metaTypes
97113
.filter(t => !EXCLUDED_META_TYPES.includes(t))
98114
.map(type => ({
@@ -102,24 +118,24 @@ export function useApiDiscovery() {
102118
group: 'Metadata',
103119
}));
104120

105-
// 5. Build per-object schema endpoints
121+
// 6. Build per-object schema endpoints
106122
const schemaEndpoints: EndpointDef[] = objectNames.map(name => ({
107123
method: 'GET' as HttpMethod,
108124
path: `/api/v1/meta/object/${name}`,
109125
desc: `${name} schema`,
110126
group: 'Metadata',
111127
}));
112128

113-
// 6. Combine all endpoints
129+
// 7. Combine all endpoints
114130
const all = [
115131
...SYSTEM_ENDPOINTS,
116-
...AUTH_ENDPOINTS,
132+
...buildAuthEndpoints(authBase),
117133
...metaEndpoints,
118134
...schemaEndpoints,
119135
...dataEndpoints,
120136
];
121137

122-
// 7. Group endpoints
138+
// 8. Group endpoints
123139
const groupMap = new Map<string, EndpointDef[]>();
124140
for (const ep of all) {
125141
const existing = groupMap.get(ep.group) || [];

0 commit comments

Comments
 (0)