| 1 | import * as Alchemy from "alchemy"; |
| 2 | import * as Drizzle from "alchemy/Drizzle"; |
| 3 | import * as GCP from "alchemy/GCP"; |
| 4 | import * as Effect from "effect/Effect"; |
| 5 | |
| 6 | export const database = Effect.gen(function* () { |
| 7 | // Shared-core, zonal, no backups: the cheapest Postgres Cloud SQL runs. |
| 8 | // No authorized networks — the only ways in are the Cloud SQL connector |
| 9 | // (Cloud Run's `/cloudsql` socket, IAM-gated) and the Data API used for |
| 10 | // migrations. |
| 11 | const instance = yield* GCP.SQL.Instance("Postgres", { |
| 12 | databaseVersion: "POSTGRES_17", |
| 13 | edition: "ENTERPRISE", |
| 14 | tier: "db-f1-micro", |
| 15 | availabilityType: "ZONAL", |
| 16 | backupEnabled: false, |
| 17 | dataApiAccess: true, |
| 18 | }); |
| 19 | const db = yield* GCP.SQL.Database("App", { |
| 20 | instance: instance.instanceName, |
| 21 | }); |
| 22 | |
| 23 | // Generated once and kept in state; the regional secret below is where |
| 24 | // the Data API (migrations) and the service (runtime) read it from. |
| 25 | const password = yield* Alchemy.Random("AppPasswordValue", { bytes: 24 }); |
| 26 | const user = yield* GCP.SQL.User("AppUser", { |
| 27 | instance: instance.instanceName, |
| 28 | password: password.text, |
| 29 | // The user owns the migrated tables, so Postgres refuses to drop it |
| 30 | // while the database still exists; deleting the instance removes it. |
| 31 | deletionPolicy: "ABANDON", |
| 32 | }); |
| 33 | // The Data API only accepts regional secrets in the instance's region. |
| 34 | const passwordSecret = yield* GCP.SecretManager.LocationsSecret( |
| 35 | "AppPassword", |
| 36 | { location: instance.region }, |
| 37 | ); |
| 38 | |
| 39 | // drizzle-kit regenerates ./migrations whenever src/schema.ts changes. |
| 40 | const schema = yield* Drizzle.Schema("Schema", { |
| 41 | schema: "./src/schema.ts", |
| 42 | out: "./migrations", |
| 43 | }); |
| 44 | |
| 45 | return { instance, db, user, password, passwordSecret, schema }; |
| 46 | }); |