Skip to content

Commit c6e79c0

Browse files
authored
Eng 403 refactor accounts (#189)
* changes to db: Person->PlatformAccount, remove old Account, Agent, add AgentIdentifier * Route changes * add trusted column to AgentIdentifier
1 parent 64cb7f5 commit c6e79c0

22 files changed

Lines changed: 566 additions & 637 deletions

File tree

apps/website/app/api/supabase/account/route.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.

apps/website/app/api/supabase/account/[id].ts renamed to apps/website/app/api/supabase/agent-identifier/[id].ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import {
44
makeDefaultDeleteHandler,
55
} from "~/utils/supabase/apiUtils";
66

7-
export const GET = makeDefaultGetHandler("Account");
7+
export const GET = makeDefaultGetHandler("AgentIdentifier");
88

99
export const OPTIONS = defaultOptionsHandler;
1010

11-
export const DELETE = makeDefaultDeleteHandler("Account");
11+
export const DELETE = makeDefaultDeleteHandler("AgentIdentifier");
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { NextResponse, NextRequest } from "next/server";
2+
3+
import { createClient } from "~/utils/supabase/server";
4+
import { getOrCreateEntity, ItemValidator } from "~/utils/supabase/dbUtils";
5+
import {
6+
createApiResponse,
7+
handleRouteError,
8+
defaultOptionsHandler,
9+
asPostgrestFailure,
10+
} from "~/utils/supabase/apiUtils";
11+
import { TablesInsert, Constants } from "@repo/database/types.gen.ts";
12+
13+
type AgentIdentifierDataInput = TablesInsert<"AgentIdentifier">;
14+
const { AgentIdentifierType } = Constants.public.Enums;
15+
16+
const agentIdentifierValidator: ItemValidator<AgentIdentifierDataInput> = (agent_identifier: any) => {
17+
if (!agent_identifier || typeof agent_identifier !== "object")
18+
return "Invalid request body: expected a JSON object.";
19+
const {
20+
identifier_type,
21+
account_id,
22+
value,
23+
trusted,
24+
} = agent_identifier;
25+
26+
if (!AgentIdentifierType.includes(identifier_type))
27+
return "Invalid identifier_type";
28+
if (!value || typeof value !== "string" || value.trim() === "")
29+
return "Missing or invalid value";
30+
if (!account_id || Number.isNaN(Number.parseInt(account_id, 10)))
31+
return "Missing or invalid account_id";
32+
if (trusted !== undefined && !["true", "false", true, false].includes(trusted))
33+
return "if included, trusted should be a boolean";
34+
35+
const keys = [ 'identifier_type', 'account_id', 'value', 'trusted' ];
36+
if (!Object.keys(agent_identifier).every((key)=>keys.includes(key)))
37+
return "Invalid agent_identifier object: extra keys";
38+
return null;
39+
};
40+
41+
export const POST = async (request: NextRequest): Promise<NextResponse> => {
42+
const supabasePromise = createClient();
43+
44+
try {
45+
const body = await request.json();
46+
const error = agentIdentifierValidator(body);
47+
if (error !== null)
48+
return createApiResponse(request, asPostgrestFailure(error, "invalid"));
49+
50+
body.account_id = Number.parseInt(body.account_id, 10);
51+
body.trusted = body.trusted === true || body.trusted === "true" || false;
52+
const supabase = await supabasePromise;
53+
const result = await getOrCreateEntity<"AgentIdentifier">({
54+
supabase,
55+
tableName: "AgentIdentifier",
56+
insertData: body as AgentIdentifierDataInput,
57+
uniqueOn: ["value", "identifier_type", "account_id"],
58+
});
59+
60+
return createApiResponse(request, result);
61+
} catch (e: unknown) {
62+
return handleRouteError(request, e, "/api/supabase/agent-identifier");
63+
}
64+
};
65+
66+
export const OPTIONS = defaultOptionsHandler;

apps/website/app/api/supabase/person/[id].ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

apps/website/app/api/supabase/person/route.ts

Lines changed: 0 additions & 85 deletions
This file was deleted.

apps/website/app/api/supabase/platform/[id].ts renamed to apps/website/app/api/supabase/platform-account/[id].ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import {
44
makeDefaultDeleteHandler,
55
} from "~/utils/supabase/apiUtils";
66

7-
export const GET = makeDefaultGetHandler("Platform");
7+
export const GET = makeDefaultGetHandler("PlatformAccount");
88

99
export const OPTIONS = defaultOptionsHandler;
1010

11-
export const DELETE = makeDefaultDeleteHandler("Platform");
11+
export const DELETE = makeDefaultDeleteHandler("PlatformAccount");
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { NextResponse, NextRequest } from "next/server";
2+
3+
import { createClient } from "~/utils/supabase/server";
4+
import { getOrCreateEntity, ItemValidator } from "~/utils/supabase/dbUtils";
5+
import {
6+
createApiResponse,
7+
handleRouteError,
8+
defaultOptionsHandler,
9+
asPostgrestFailure,
10+
} from "~/utils/supabase/apiUtils";
11+
import { TablesInsert, Constants } from "@repo/database/types.gen.ts";
12+
13+
const { AgentType, Platform } = Constants.public.Enums;
14+
15+
type PlatformAccountDataInput = TablesInsert<"PlatformAccount">;
16+
17+
const accountValidator: ItemValidator<PlatformAccountDataInput> = (account: any) => {
18+
if (!account || typeof account !== "object")
19+
return "Invalid request body: expected a JSON object.";
20+
const { name, platform, account_local_id, write_permission, active, agent_type, metadata, dg_account } = account;
21+
22+
if (!name || typeof name !== "string" || name.trim() === "")
23+
return "Missing or invalid name";
24+
// This is not dry, to be rewritten with Drizzle/Zed.
25+
if (!Platform.includes(platform))
26+
return "Missing or invalid platform";
27+
if (agent_type !== undefined && !AgentType.includes(agent_type))
28+
return "Invalid agent_type";
29+
if (write_permission !== undefined && typeof write_permission != 'boolean')
30+
return "write_permission must be boolean";
31+
if (active !== undefined && typeof active != 'boolean')
32+
return "active must be boolean";
33+
if (metadata !== undefined) {
34+
if (typeof metadata != 'string')
35+
return "metadata should be a JSON string";
36+
else try {
37+
JSON.parse(metadata)
38+
} catch (error) {
39+
return "metadata should be a JSON string";
40+
}
41+
}
42+
if (!account_local_id || typeof account_local_id != "string" || account_local_id.trim() === "")
43+
return "Missing or invalid account_local_id";
44+
if (dg_account != undefined) {
45+
if (typeof dg_account != "string")
46+
return "dg_account should be a UUID string";
47+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
48+
if (!uuidRegex.test(dg_account))
49+
return "dg_account must be a valid UUID";
50+
}
51+
const keys = [ 'name', 'platform', 'account_local_id', 'write_permission', 'active', 'agent_type', 'metadata', 'dg_account' ];
52+
if (!Object.keys(account).every((key)=>keys.includes(key)))
53+
return "Invalid account object: extra keys";
54+
return null;
55+
};
56+
57+
export const POST = async (request: NextRequest): Promise<NextResponse> => {
58+
const supabasePromise = createClient();
59+
60+
try {
61+
const body = await request.json();
62+
const error = accountValidator(body);
63+
if (error !== null)
64+
return createApiResponse(request, asPostgrestFailure(error, "invalid"));
65+
66+
const supabase = await supabasePromise;
67+
const result = await getOrCreateEntity<"PlatformAccount">({
68+
supabase,
69+
tableName: "PlatformAccount",
70+
insertData: body as PlatformAccountDataInput,
71+
uniqueOn: ["account_local_id", "platform"],
72+
});
73+
74+
return createApiResponse(request, result);
75+
} catch (e: unknown) {
76+
return handleRouteError(request, e, "/api/supabase/platfor-account");
77+
}
78+
};
79+
80+
export const OPTIONS = defaultOptionsHandler;

0 commit comments

Comments
 (0)