Skip to content

Commit

Permalink
feature: crud provider apikeys
Browse files Browse the repository at this point in the history
Implements basic crud for apikeys
  • Loading branch information
geclos committed Jul 29, 2024
1 parent dda49f1 commit 3179423
Show file tree
Hide file tree
Showing 38 changed files with 4,381 additions and 19 deletions.
26 changes: 26 additions & 0 deletions apps/web/src/actions/providerApiKeuys/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use server'

import { createProviderApiKey } from '@latitude-data/core'
import { Providers } from '@latitude-data/core/browser'
import { z } from 'zod'

import { authProcedure } from '../procedures'

export const createProviderApiKeyAction = authProcedure
.createServerAction()
.input(
z.object({
provider: z.string(),
token: z.string(),
name: z.string(),
}),
)
.handler(async ({ input, ctx }) => {
return await createProviderApiKey({
workspace: ctx.workspace,
provider: input.provider as Providers,
token: input.token,
name: input.name,
authorId: ctx.user.id,
}).then((r) => r.unwrap())
})
27 changes: 27 additions & 0 deletions apps/web/src/actions/providerApiKeuys/destroy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use server'

import {
destroyProviderApiKey,
ProviderApiKeysRepository,
} from '@latitude-data/core'
import { z } from 'zod'

import { authProcedure } from '../procedures'

export const destroyProviderApiKeyAction = authProcedure
.createServerAction()
.input(
z.object({
id: z.number(),
}),
)
.handler(async ({ input, ctx }) => {
const providerApiKeysRepository = new ProviderApiKeysRepository(
ctx.workspace.id,
)
const apiKeyProvider = await providerApiKeysRepository
.find(input.id)
.then((r) => r.unwrap())

return await destroyProviderApiKey(apiKeyProvider).then((r) => r.unwrap())
})
13 changes: 13 additions & 0 deletions apps/web/src/actions/providerApiKeuys/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use server'

import { ProviderApiKeysRepository } from '@latitude-data/core'

import { authProcedure } from '../procedures'

export const getProviderApiKeyAction = authProcedure
.createServerAction()
.handler(async ({ ctx }) => {
const providerApiKeysScope = new ProviderApiKeysRepository(ctx.workspace.id)

return await providerApiKeysScope.findAll().then((r) => r.unwrap())
})
13 changes: 13 additions & 0 deletions apps/web/src/actions/users/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use server'

import { UsersRepository } from '@latitude-data/core'

import { authProcedure } from '../procedures'

export const getUsersActions = authProcedure
.createServerAction()
.handler(async ({ ctx }) => {
const usersScope = new UsersRepository(ctx.workspace.id)

return await usersScope.findAll().then((r) => r.unwrap())
})
2 changes: 1 addition & 1 deletion apps/web/src/actions/workspaces/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const updateWorkspaceAction = authProcedure
const userId = ctx.session.userId
const workspacesScope = new WorkspacesRepository(userId)
const workspace = await workspacesScope
.getWorkspaceById(input.workspaceId)
.find(input.workspaceId)
.then((r) => r.unwrap())

const updatedWorkspace = await updateWorkspace({
Expand Down
15 changes: 14 additions & 1 deletion apps/web/src/app/(private)/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,22 @@

import { Settings } from '@latitude-data/web-ui'
import useCurrentWorkspace from '$/stores/currentWorkspace'
import useProviderApiKeys from '$/stores/providerApiKeys'
import useUsers from '$/stores/users'

export default function SettingsPage() {
const { data, update } = useCurrentWorkspace()
const { data: apiKeys, create, destroy } = useProviderApiKeys()
const { data: users } = useUsers()

return <Settings workspace={data!} updateWorkspace={update} />
return (
<Settings
users={users}
workspace={data}
updateWorkspace={update}
apiKeys={apiKeys}
createApiKey={create}
destroyApiKey={destroy}
/>
)
}
2 changes: 1 addition & 1 deletion apps/web/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function middleware(request: LatitudeRequest) {
return apiUnauthorized()
}

const result = await unsafelyGetApiKey({ uuid: token })
const result = await unsafelyGetApiKey({ token })
if (result.error) return apiUnauthorized()

request.workspaceId = result.value.workspaceId
Expand Down
10 changes: 9 additions & 1 deletion apps/web/src/stores/currentWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@ export default function useCurrentWorkspace(opts?: SWRConfiguration) {
workspaceId: data!.id,
name: payload.name,
})
if (error) throw error
if (error) {
toast({
title: 'Failed to update workspace name',
description: error.message,
variant: 'destructive',
})

return
}

toast({
title: 'Name updated to ' + payload.name,
Expand Down
97 changes: 97 additions & 0 deletions apps/web/src/stores/providerApiKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useCallback } from 'react'

import { ProviderApiKey } from '@latitude-data/core'
import { Providers } from '@latitude-data/core/browser'
import { useToast } from '@latitude-data/web-ui'
import { createProviderApiKeyAction } from '$/actions/providerApiKeuys/create'
import { destroyProviderApiKeyAction } from '$/actions/providerApiKeuys/destroy'
import { getProviderApiKeyAction } from '$/actions/providerApiKeuys/fetch'
import useSWR, { SWRConfiguration } from 'swr'

export default function useProviderApiKeys(opts?: SWRConfiguration) {
const { toast } = useToast()
const key = 'api/providerApiKeys'
const fetcher = async () => {
const [data, error] = await getProviderApiKeyAction()
if (error) {
toast({
title: 'Error',
description: error.message,
variant: 'destructive',
})

return []
}

return data
}
const {
data = [],
mutate,
...rest
} = useSWR<ProviderApiKey[]>(key, fetcher, opts)
const create = useCallback(
async ({
name,
provider,
token,
}: {
name: string
provider: Providers
token: string
}) => {
const [apikey, error] = await createProviderApiKeyAction({
provider,
token,
name,
})

if (error) {
toast({
title: 'Error',
description: error.message,
variant: 'destructive',
})

return
}

mutate([...data, apikey])

toast({
title: 'Success',
description: 'API Key ' + apikey.name + ' created',
})

return apikey
},
[data, mutate],
)

const destroy = useCallback(
async (id: number) => {
const [apikey, error] = await destroyProviderApiKeyAction({ id })
if (error) {
toast({
title: 'Error',
description: error.message,
variant: 'destructive',
})

return
}

mutate(data.filter((apikey) => apikey.id !== id))

toast({
title: 'Success',
description: 'API Key ' + apikey!.name + ' deleted',
})

return apikey!
},
[data, mutate],
)

return { data, create, destroy, mutate, ...rest }
}
27 changes: 27 additions & 0 deletions apps/web/src/stores/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { User } from '@latitude-data/core'
import { useToast } from '@latitude-data/web-ui'
import { getUsersActions } from '$/actions/users/fetch'
import useSWR, { SWRConfiguration } from 'swr'

export default function useUsers(opts?: SWRConfiguration) {
const { toast } = useToast()

const fetcher = async () => {
const [data, error] = await getUsersActions()
if (error) {
toast({
title: 'Error',
description: error.message,
variant: 'destructive',
})

return []
}

return data
}

const { data = [], ...rest } = useSWR<User[]>('api/users', fetcher, opts)

return { data, ...rest }
}
25 changes: 25 additions & 0 deletions packages/core/drizzle/0008_mean_wallop.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
DO $$ BEGIN
CREATE TYPE "public"."provider" AS ENUM('openai', 'anthropic');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "latitude"."providerApiKeys" (
"id" bigserial PRIMARY KEY NOT NULL,
"uuid" uuid NOT NULL,
"provider" "provider" NOT NULL,
"workspace_id" bigint NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "latitude"."api_keys" RENAME COLUMN "uuid" TO "token";--> statement-breakpoint
ALTER TABLE "latitude"."api_keys" DROP CONSTRAINT "api_keys_uuid_unique";--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "latitude"."providerApiKeys" ADD CONSTRAINT "providerApiKeys_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "latitude"."workspaces"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "provider_apikeys_workspace_id_idx" ON "latitude"."providerApiKeys" USING btree ("workspace_id");--> statement-breakpoint
ALTER TABLE "latitude"."api_keys" ADD CONSTRAINT "api_keys_token_unique" UNIQUE("token");
2 changes: 2 additions & 0 deletions packages/core/drizzle/0009_cheerful_sir_ram.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE "latitude"."providerApiKeys" RENAME COLUMN "uuid" TO "token";--> statement-breakpoint
ALTER TABLE "latitude"."providerApiKeys" ALTER COLUMN "token" SET DATA TYPE varchar;
1 change: 1 addition & 0 deletions packages/core/drizzle/0010_flippant_ma_gnuci.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "latitude"."providerApiKeys" ADD COLUMN "name" varchar NOT NULL;
8 changes: 8 additions & 0 deletions packages/core/drizzle/0011_nosy_mystique.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ALTER TABLE "latitude"."providerApiKeys" RENAME TO "provider_api_keys";--> statement-breakpoint
ALTER TABLE "latitude"."provider_api_keys" DROP CONSTRAINT "providerApiKeys_workspace_id_workspaces_id_fk";
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "latitude"."provider_api_keys" ADD CONSTRAINT "provider_api_keys_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "latitude"."workspaces"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
9 changes: 9 additions & 0 deletions packages/core/drizzle/0012_common_fat_cobra.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ALTER TABLE "latitude"."provider_api_keys" ADD COLUMN "author_id" text NOT NULL;--> statement-breakpoint
ALTER TABLE "latitude"."provider_api_keys" ADD COLUMN "last_used_at" timestamp;--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "latitude"."provider_api_keys" ADD CONSTRAINT "provider_api_keys_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "latitude"."users"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "provider_apikeys_user_id_idx" ON "latitude"."provider_api_keys" USING btree ("author_id");
Loading

0 comments on commit 3179423

Please sign in to comment.