1import * as Presign from "@distilled.cloud/aws/Presign";
2import * as PgClient from "@effect/sql-pg/PgClient";
3import * as Alchemy from "alchemy";
4import * as Effect from "effect/Effect";
5import * as Redacted from "effect/Redacted";
6
7export const BootstrapDatabase = Alchemy.Action(
8 "BootstrapDatabase",
9 Effect.fn(function* (input: {
10 endpoint: string;
11 roleArn: string;
12 version: string;
13 }) {
14 const token = yield* Presign.presignUrl({
15 method: "GET",
16 url: `https://${input.endpoint}/?Action=DbConnectAdmin`,
17 service: "dsql",
18 expiresIn: 900,
19 });
20 yield* Effect.gen(function* () {
21 const sql = yield* PgClient.PgClient;
22 // DSQL requires separate autocommit statements for catalog changes.
23 yield* sql`CREATE SCHEMA IF NOT EXISTS app`;
24 const roles =
25 yield* sql`SELECT rolname FROM pg_roles WHERE rolname = 'app_user'`;
26 if (roles.length === 0) {
27 yield* sql`CREATE ROLE app_user WITH LOGIN`;
28 }
29 yield* sql`CREATE TABLE IF NOT EXISTS app.todos (
30 id uuid PRIMARY KEY,
31 text text NOT NULL,
32 done boolean NOT NULL DEFAULT false,
33 created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
34 )`;
35 yield* sql`GRANT USAGE ON SCHEMA app TO app_user`;
36 yield* sql`GRANT SELECT, INSERT, UPDATE, DELETE ON app.todos TO app_user`;
37 const mappings = yield* sql`SELECT arn FROM sys.iam_pg_role_mappings
38 WHERE pg_role_name = 'app_user' AND arn = ${input.roleArn}`;
39 if (mappings.length === 0) {
40 yield* sql.unsafe(
41 `AWS IAM GRANT app_user TO '${input.roleArn.replaceAll("'", "''")}'`,
42 );
43 }
44 }).pipe(
45 Effect.provide(
46 PgClient.layer({
47 host: input.endpoint,
48 port: 5432,
49 database: "postgres",
50 username: "admin",
51 password: Redacted.make(token.slice("https://".length)),
52 ssl: { rejectUnauthorized: true, servername: input.endpoint },
53 }),
54 ),
55 );
56 return input.version;
57 }),
58);