diff --git a/cmd/internal/client/client.go b/cmd/internal/client/client.go index fe49a6e8..c41807af 100644 --- a/cmd/internal/client/client.go +++ b/cmd/internal/client/client.go @@ -622,7 +622,7 @@ func (c *Client) createMatter( onConflict := openapi.CreateObjectJSONBodyOnConflictRename res, err := c.api.CreateObjectWithResponse(ctx, openapi.CreateObjectJSONRequestBody{ Name: name, - Type: contentType, + Type: &contentType, Size: &sizeInt, Parent: &parent, Dirtype: &dirtype, diff --git a/cmd/internal/openapi/client.gen.go b/cmd/internal/openapi/client.gen.go index c19e3f73..a4408e7e 100644 --- a/cmd/internal/openapi/client.gen.go +++ b/cmd/internal/openapi/client.gen.go @@ -3708,7 +3708,7 @@ type CreateObjectJSONBody struct { Parent *string `json:"parent,omitempty"` Size *int `json:"size,omitempty"` StorageId *string `json:"storageId,omitempty"` - Type string `json:"type"` + Type *string `json:"type,omitempty"` } // CreateObjectJSONBodyOnConflict defines parameters for CreateObject. diff --git a/server/adapters/repos/api-key-scopes.ts b/server/adapters/repos/api-key-scopes.ts index 90b27fca..6f2dc72a 100644 --- a/server/adapters/repos/api-key-scopes.ts +++ b/server/adapters/repos/api-key-scopes.ts @@ -1,5 +1,5 @@ import { type ApiKeyScope, ApiKeyTemplate, apiKeyMetadata, parseApiKeyScope } from '@shared/api-key-templates' -import { and, eq, inArray, isNull } from 'drizzle-orm' +import { and, eq, inArray } from 'drizzle-orm' import { apikey, member } from '../../db/auth-schema' import type { Database } from '../../platform/interface' import { resolveOrganizationOwnerUserId } from './organization-owner' @@ -56,13 +56,22 @@ export async function normalizeLegacyApiKey( } export async function normalizeLegacyApiKeysForUser(db: Database, userId: string): Promise { - await db - .update(apikey) - .set({ - metadata: JSON.stringify(apiKeyMetadata({ mode: 'user-workspaces' })), - updatedAt: new Date(), - }) - .where(and(eq(apikey.configId, ApiKeyTemplate.WEBDAV), eq(apikey.referenceId, userId), isNull(apikey.metadata))) + const webDavKeys = await db + .select({ id: apikey.id, metadata: apikey.metadata }) + .from(apikey) + .where(and(eq(apikey.configId, ApiKeyTemplate.WEBDAV), eq(apikey.referenceId, userId))) + const legacyWebDavKeyIds = webDavKeys + .filter(({ metadata }) => scopeForApiKey(ApiKeyTemplate.WEBDAV, parseMetadata(metadata)) === null) + .map(({ id }) => id) + if (legacyWebDavKeyIds.length > 0) { + await db + .update(apikey) + .set({ + metadata: JSON.stringify(apiKeyMetadata({ mode: 'user-workspaces' })), + updatedAt: new Date(), + }) + .where(inArray(apikey.id, legacyWebDavKeyIds)) + } const ownedOrgs = await db .select({ orgId: member.organizationId }) @@ -78,7 +87,7 @@ export async function normalizeLegacyApiKeysForUser(db: Database, userId: string metadata: JSON.stringify(apiKeyMetadata({ mode: 'workspace', orgId })), updatedAt: new Date(), }) - .where(and(inArray(apikey.configId, WORKSPACE_TEMPLATES), eq(apikey.referenceId, orgId), isNull(apikey.metadata))) + .where(and(inArray(apikey.configId, WORKSPACE_TEMPLATES), eq(apikey.referenceId, orgId))) } } diff --git a/server/adapters/repos/api-keys-rate-limit.integration.test.ts b/server/adapters/repos/api-keys-rate-limit.integration.test.ts index aca96a14..1784d13a 100644 --- a/server/adapters/repos/api-keys-rate-limit.integration.test.ts +++ b/server/adapters/repos/api-keys-rate-limit.integration.test.ts @@ -166,11 +166,16 @@ describe('API keys', () => { const { orgId, userId } = await getUserAndOrg(db) const remoteDownload = await createOrgApiKey(auth, 'remote-download', orgId, userId) const ihost = await createOrgApiKey(auth, 'ihost', orgId, userId) + // biome-ignore lint/suspicious/noExplicitAny: better-auth plugin API is not fully typed + const webdav = (await (auth.api as any).createApiKey({ + body: { configId: 'webdav', userId }, + })) as { id: string } await db.run(sql` UPDATE apikey - SET reference_id = ${orgId}, metadata = NULL + SET reference_id = ${orgId}, metadata = '{}' WHERE id IN (${remoteDownload.id}, ${ihost.id}) `) + await db.run(sql`UPDATE apikey SET metadata = '{}' WHERE id = ${webdav.id}`) await expect(apiKeys.verifyApiKey(auth, db, remoteDownload.key, 'remote-download')).resolves.toMatchObject({ referenceId: userId, @@ -182,8 +187,21 @@ describe('API keys', () => { const listResponse = await app.request('/api/auth/api-key/list', { headers }) expect(listResponse.status).toBe(200) - const listed = (await listResponse.json()) as { apiKeys: Array<{ id: string }> } - expect(listed.apiKeys.map((key) => key.id)).toEqual(expect.arrayContaining([remoteDownload.id, ihost.id])) + const listed = (await listResponse.json()) as { + apiKeys: Array<{ id: string; metadata: { scope: { mode: string; orgId?: string } } }> + } + expect(listed.apiKeys).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: ihost.id, + metadata: { scope: { mode: 'workspace', orgId } }, + }), + expect.objectContaining({ + id: webdav.id, + metadata: { scope: { mode: 'user-workspaces' } }, + }), + ]), + ) expect(await getApiKeyRow(db, ihost.id)).toMatchObject({ reference_id: userId }) })