@percepta/create 3.6.1 → 3.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -6
- package/dist/{git-ops-C2CIjuce.js → git-ops-BD7JNnal.js} +1 -1
- package/dist/{git-ops-C2CIjuce.js.map → git-ops-BD7JNnal.js.map} +1 -1
- package/dist/github-RCIMUq70.js +131 -0
- package/dist/github-RCIMUq70.js.map +1 -0
- package/dist/index.js +63 -122
- package/dist/index.js.map +1 -1
- package/dist/{init-sI9aIrkU.js → init-COp0nGdk.js} +4 -2
- package/dist/{init-sI9aIrkU.js.map → init-COp0nGdk.js.map} +1 -1
- package/dist/manifest-CqIDnbgs.js +58 -0
- package/dist/manifest-CqIDnbgs.js.map +1 -0
- package/dist/register-app-C7ZBpAaZ.js +103 -0
- package/dist/register-app-C7ZBpAaZ.js.map +1 -0
- package/dist/register-os-blueprint-DGjBUZYa.js +90 -0
- package/dist/register-os-blueprint-DGjBUZYa.js.map +1 -0
- package/dist/{status-CKe4aKso.js → status-BXYaQ4a2.js} +3 -3
- package/dist/{status-CKe4aKso.js.map → status-BXYaQ4a2.js.map} +1 -1
- package/dist/{sync-D1vkoofl.js → sync-BayU4w1j.js} +3 -3
- package/dist/{sync-D1vkoofl.js.map → sync-BayU4w1j.js.map} +1 -1
- package/dist/template-versions-CEIP9vhl.js +35 -0
- package/dist/template-versions-CEIP9vhl.js.map +1 -0
- package/dist/{upstream-gUHLWSR1.js → upstream-CZEzLrS4.js} +3 -3
- package/dist/{upstream-gUHLWSR1.js.map → upstream-CZEzLrS4.js.map} +1 -1
- package/dist/validate-dssldJAj.js +14 -0
- package/dist/validate-dssldJAj.js.map +1 -0
- package/package.json +1 -1
- package/template-versions.json +2 -2
- package/templates/infra/os.blueprint.yaml.template +138 -0
- package/templates/monorepo/README.md +41 -3
- package/templates/monorepo/auth/README.md +6 -3
- package/templates/monorepo/auth/package.json +2 -4
- package/templates/monorepo/auth/src/config/database.ts +1 -1
- package/templates/{webapp → monorepo}/docker-compose.yml +2 -2
- package/templates/monorepo/package.json.template +5 -2
- package/templates/monorepo/pnpm-workspace.yaml +4 -0
- package/templates/monorepo/scripts/setup-local-databases.mjs +183 -0
- package/templates/webapp/AGENTS.md +13 -20
- package/templates/webapp/README.md +32 -36
- package/templates/webapp/agent-skills/database.md +21 -21
- package/templates/webapp/agent-skills/langfuse.md +7 -7
- package/templates/webapp/agent-skills/llm.md +4 -2
- package/templates/webapp/agent-skills/oneshot.md +7 -6
- package/templates/webapp/agent-skills/ryvn.md +12 -16
- package/templates/webapp/deploy/README.md +10 -51
- package/templates/webapp/drizzle.config.ts +2 -23
- package/templates/webapp/env.example.template +8 -14
- package/templates/webapp/package.json.template +8 -15
- package/templates/webapp/scripts/start.sh +12 -16
- package/templates/webapp/src/config/getEnvConfig.ts +4 -10
- package/templates/webapp/src/drizzle/db.ts +6 -21
- package/templates/webapp/src/startup-checks.ts +28 -7
- package/templates/monorepo/auth/scripts/setup-database.ts +0 -11
- package/templates/webapp/.github/workflows/__APP_NAME__-terraform-ryvn-release.yaml +0 -92
- package/templates/webapp/agent-skills/deploy.md +0 -92
- package/templates/webapp/deploy/ryvn/__APP_NAME__-terraform.service.yaml +0 -10
- package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__-terraform.env.percepta-test.serviceinstallation.yaml +0 -11
- package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__.env.percepta-test.serviceinstallation.yaml +0 -154
- package/templates/webapp/terraform/README.md +0 -147
- package/templates/webapp/terraform/deploy.sh +0 -97
- package/templates/webapp/terraform/main.tf +0 -101
- package/templates/webapp/terraform/modules/cloudtrail/main.tf +0 -27
- package/templates/webapp/terraform/modules/cloudtrail/outputs.tf +0 -10
- package/templates/webapp/terraform/modules/cloudtrail/variables.tf +0 -15
- package/templates/webapp/terraform/modules/networking/main.tf +0 -118
- package/templates/webapp/terraform/modules/networking/outputs.tf +0 -38
- package/templates/webapp/terraform/modules/networking/variables.tf +0 -24
- package/templates/webapp/terraform/modules/rds/main.tf +0 -227
- package/templates/webapp/terraform/modules/rds/outputs.tf +0 -73
- package/templates/webapp/terraform/modules/rds/variables.tf +0 -61
- package/templates/webapp/terraform/modules/s3-logging/main.tf +0 -148
- package/templates/webapp/terraform/modules/s3-logging/outputs.tf +0 -10
- package/templates/webapp/terraform/modules/s3-logging/variables.tf +0 -16
- package/templates/webapp/terraform/modules/secrets/main.tf +0 -39
- package/templates/webapp/terraform/modules/secrets/outputs.tf +0 -9
- package/templates/webapp/terraform/modules/secrets/variables.tf +0 -51
- package/templates/webapp/terraform/outputs.tf +0 -102
- package/templates/webapp/terraform/providers.tf +0 -32
- package/templates/webapp/terraform/schema/main.tf +0 -4
- package/templates/webapp/terraform/schema/outputs.tf +0 -9
- package/templates/webapp/terraform/schema/variables.tf +0 -19
- package/templates/webapp/terraform/schema/versions.tf +0 -38
- package/templates/webapp/terraform/terraform.tfvars.example +0 -65
- package/templates/webapp/terraform/variables.tf +0 -129
|
@@ -38,11 +38,9 @@ export * from "./documents";
|
|
|
38
38
|
pnpm db:generate
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
This creates a new SQL migration file and normalizes generated
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
automatically but you should verify it's correct, especially for new foreign
|
|
45
|
-
keys and destructive changes.
|
|
41
|
+
This creates a new SQL migration file and normalizes generated SQL. **Review
|
|
42
|
+
the generated SQL** — Drizzle generates it automatically but you should verify
|
|
43
|
+
it's correct, especially for new foreign keys and destructive changes.
|
|
46
44
|
|
|
47
45
|
### 4. Apply the migration
|
|
48
46
|
|
|
@@ -95,21 +93,25 @@ If `createTransaction` is called while already inside a transaction, it reuses t
|
|
|
95
93
|
|
|
96
94
|
## Running PostgreSQL Locally
|
|
97
95
|
|
|
98
|
-
### Start with
|
|
96
|
+
### Start with root setup
|
|
99
97
|
|
|
100
98
|
```bash
|
|
101
|
-
pnpm
|
|
99
|
+
pnpm run setup
|
|
102
100
|
```
|
|
103
101
|
|
|
104
|
-
This
|
|
102
|
+
This delegates to the monorepo root. Root setup starts PostgreSQL 16 on port
|
|
103
|
+
**5434** and SpiceDB on **50051**, creates the shared auth database plus each
|
|
104
|
+
app database discovered from `packages/*/.env.local`, then runs migrations and
|
|
105
|
+
seeds.
|
|
105
106
|
|
|
106
|
-
###
|
|
107
|
+
### Run migrations
|
|
107
108
|
|
|
108
109
|
```bash
|
|
109
|
-
pnpm db:
|
|
110
|
+
pnpm db:migrate
|
|
110
111
|
```
|
|
111
112
|
|
|
112
|
-
This
|
|
113
|
+
This applies all pending migrations. Database creation is owned by the local
|
|
114
|
+
infrastructure setup, not by the app migration command.
|
|
113
115
|
|
|
114
116
|
### Inspect the database
|
|
115
117
|
|
|
@@ -119,27 +121,25 @@ pnpm db:studio
|
|
|
119
121
|
|
|
120
122
|
Opens Drizzle Studio in the browser — a visual database explorer.
|
|
121
123
|
|
|
122
|
-
### Stop
|
|
124
|
+
### Stop local services
|
|
123
125
|
|
|
124
126
|
```bash
|
|
125
|
-
pnpm docker:down
|
|
127
|
+
pnpm --dir ../.. run docker:down
|
|
126
128
|
```
|
|
127
129
|
|
|
128
130
|
## Environment Variables
|
|
129
131
|
|
|
130
132
|
| Variable | Default | Description |
|
|
131
133
|
|----------|---------|-------------|
|
|
132
|
-
| `
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
| `DATABASE_SCHEMA` | - | Optional Postgres schema/search path |
|
|
138
|
-
| `DATABASE_USE_SSL` | `false` | Enable SSL connections |
|
|
134
|
+
| `DATABASE_URL` | `postgresql://postgres:postgres@localhost:5434/__DB_NAME__` | App PostgreSQL connection URL |
|
|
135
|
+
|
|
136
|
+
Shared auth data belongs to the customer monorepo auth package. Deployed apps
|
|
137
|
+
should receive `AUTH_DATABASE_URL` from the monorepo auth Secret; do not reuse
|
|
138
|
+
`DATABASE_URL` for auth.
|
|
139
139
|
|
|
140
140
|
## Key Concepts
|
|
141
141
|
|
|
142
142
|
- **Schemas are TypeScript, migrations are SQL.** You define tables in TS, then `pnpm db:generate` creates the SQL diff. Never hand-write migration SQL.
|
|
143
143
|
- **DatabaseService is a singleton.** Call `DatabaseService.create()` anywhere — it always returns the same instance with the same connection pool.
|
|
144
144
|
- **Transaction propagation is automatic.** Code inside `createTransaction` gets a transaction; code outside gets the raw connection. `getDatabase()` returns whichever is active.
|
|
145
|
-
- **
|
|
145
|
+
- **Root-owned local infra.** The monorepo root Docker Compose uses port 5434 to avoid conflicting with any system PostgreSQL on 5432.
|
|
@@ -60,11 +60,11 @@ This is a singleton — `create()` returns the same instance every time. It grac
|
|
|
60
60
|
|
|
61
61
|
## Getting Langfuse Keys
|
|
62
62
|
|
|
63
|
-
###
|
|
63
|
+
### Shared Instances
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
If the target stack provides a shared Langfuse instance, store
|
|
66
|
+
`LANGFUSE_PUBLIC_KEY` and sensitive `LANGFUSE_SECRET_KEY` through that
|
|
67
|
+
platform's environment-scoped variable or secret mechanism.
|
|
68
68
|
|
|
69
69
|
### Self-Hosted / Langfuse Cloud
|
|
70
70
|
|
|
@@ -74,17 +74,17 @@ For external projects, you can:
|
|
|
74
74
|
|
|
75
75
|
## Running Locally
|
|
76
76
|
|
|
77
|
-
### Option 1: Use
|
|
77
|
+
### Option 1: Use a Shared Langfuse Instance
|
|
78
78
|
|
|
79
79
|
Set the keys in `.env.local` if you want local traces to go to the shared instance:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
LANGFUSE_BASE_URL=https://langfuse.
|
|
82
|
+
LANGFUSE_BASE_URL=https://langfuse.example.com
|
|
83
83
|
LANGFUSE_PUBLIC_KEY=pk-lf-...
|
|
84
84
|
LANGFUSE_SECRET_KEY=sk-lf-...
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
Traces from local dev appear in the shared dashboard under your project.
|
|
87
|
+
Traces from local dev appear in the configured shared dashboard under your project.
|
|
88
88
|
|
|
89
89
|
### Option 2: Run Langfuse locally
|
|
90
90
|
|
|
@@ -40,7 +40,9 @@ The shared `@percepta/ai` provider helper chooses a provider at call time:
|
|
|
40
40
|
|
|
41
41
|
## Deployment
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Provider keys and Langfuse credentials should come from the target deployment
|
|
44
|
+
platform. Do not commit shared provider keys or generated environment-specific
|
|
45
|
+
secret files to the app repo.
|
|
44
46
|
|
|
45
47
|
## Local Development
|
|
46
48
|
|
|
@@ -54,6 +56,6 @@ ANTHROPIC_API_KEY=sk-ant-...
|
|
|
54
56
|
|
|
55
57
|
## Observability
|
|
56
58
|
|
|
57
|
-
`LLMService` enables AI SDK telemetry by default and attaches provider/model metadata to each call. When `LANGFUSE_*` values are configured, the template's OpenTelemetry bootstrap sends LLM spans to Langfuse.
|
|
59
|
+
`LLMService` enables AI SDK telemetry by default and attaches provider/model metadata to each call. When `LANGFUSE_*` values are configured, the template's OpenTelemetry bootstrap sends LLM spans to Langfuse.
|
|
58
60
|
|
|
59
61
|
Use a stable `telemetryFunctionId` for every meaningful LLM operation, such as `extract-invoice-fields` or `draft-member-message`.
|
|
@@ -142,8 +142,7 @@ Once all chunks are implemented, verify the app runs end-to-end locally.
|
|
|
142
142
|
### Step 1: Start dependencies
|
|
143
143
|
|
|
144
144
|
```bash
|
|
145
|
-
pnpm
|
|
146
|
-
pnpm db:setup-and-migrate
|
|
145
|
+
pnpm run setup
|
|
147
146
|
```
|
|
148
147
|
|
|
149
148
|
If the app uses Inngest functions, start the local Inngest dev server in a separate terminal:
|
|
@@ -192,19 +191,21 @@ git init
|
|
|
192
191
|
git add -A
|
|
193
192
|
git commit -m "Initial implementation of <app-name>"
|
|
194
193
|
|
|
195
|
-
# Create the repo
|
|
196
|
-
gh repo create
|
|
194
|
+
# Create and push the repo in the target GitHub org
|
|
195
|
+
gh repo create <org>/<app-name> --internal --source=. --push
|
|
197
196
|
```
|
|
198
197
|
|
|
199
198
|
If `gh` is not authenticated, tell the user to run `gh auth login` and then continue.
|
|
200
199
|
|
|
201
200
|
---
|
|
202
201
|
|
|
203
|
-
## Phase 6: Deploy
|
|
202
|
+
## Phase 6: Deploy (When Requested)
|
|
204
203
|
|
|
205
204
|
Only do this when the user explicitly asks to deploy.
|
|
206
205
|
|
|
207
|
-
|
|
206
|
+
Deployment is stack-specific. Confirm the target environment and use the
|
|
207
|
+
customer infra repo or deployment guide for that stack. Blueberry does not
|
|
208
|
+
generate environment-specific installation YAML.
|
|
208
209
|
|
|
209
210
|
For Ryvn CLI operations (checking status, logs, troubleshooting), use the `/use-ryvn` skill.
|
|
210
211
|
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
# Ryvn
|
|
1
|
+
# Ryvn
|
|
2
2
|
|
|
3
|
-
Ryvn
|
|
3
|
+
Ryvn can be used to register and release this app as a service. Blueberry does
|
|
4
|
+
not generate a target environment installation because those details are
|
|
5
|
+
customer and stack specific.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Generated Files
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
| `percepta-test` | Internal dev/test | `<app>.percepta-test.aitco.dev` |
|
|
9
|
+
- `deploy/ryvn/__APP_NAME__.service.yaml` declares the app service and Docker build context.
|
|
10
|
+
- `.github/workflows/__APP_NAME__-ryvn-release.yaml` builds the image and creates a Ryvn release.
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
## Environment Installations
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
Environment installations belong in the infra repo. A customer blueprint should
|
|
15
|
+
compose the required base infrastructure, monorepo infrastructure, app database
|
|
16
|
+
registration, runtime services, ingress, secrets, and the app service
|
|
17
|
+
installation for the target stack.
|
|
16
18
|
|
|
17
19
|
## Ryvn CLI
|
|
18
20
|
|
|
19
21
|
For all Ryvn CLI operations (checking status, viewing logs, troubleshooting, managing installations), use the **`/use-ryvn`** skill. It has comprehensive CLI reference docs and handles authentication, deployment, configuration, and operations.
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
# Quick status check
|
|
23
|
-
ryvn get installation __APP_NAME__ -e percepta-test
|
|
24
|
-
ryvn logs __APP_NAME__ -e percepta-test
|
|
25
|
-
```
|
|
@@ -1,66 +1,25 @@
|
|
|
1
1
|
# Deploy
|
|
2
2
|
|
|
3
|
-
This directory
|
|
3
|
+
This directory contains environment-neutral deployment metadata for __APP_TITLE__.
|
|
4
|
+
It intentionally does not include customer or environment installation files.
|
|
4
5
|
|
|
5
6
|
```
|
|
6
7
|
deploy/
|
|
7
8
|
└── ryvn/
|
|
8
|
-
|
|
9
|
-
├── __APP_NAME__-terraform.service.yaml
|
|
10
|
-
└── environments/
|
|
11
|
-
└── percepta-test/
|
|
12
|
-
└── installations/
|
|
13
|
-
├── __APP_NAME__.env.percepta-test.serviceinstallation.yaml
|
|
14
|
-
└── __APP_NAME__-terraform.env.percepta-test.serviceinstallation.yaml
|
|
9
|
+
└── __APP_NAME__.service.yaml
|
|
15
10
|
```
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
The `pnpm deploy:percepta-test` script delegates to the versioned `@percepta/deploy` CLI. The app owns only the Ryvn service/installation YAML and secrets env file. The helper talks directly to Ryvn: it preflights the existing platform services, creates/updates the services, runs the GitHub Actions release workflows, creates the schema installation, approves the schema Terraform plan, creates or updates app-scoped Ryvn secrets, creates the web installation, waits for health, and verifies the health and app routes.
|
|
22
|
-
|
|
23
|
-
## Deploying
|
|
24
|
-
|
|
25
|
-
Tell Claude "deploy this app to percepta-test" or run:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pnpm deploy:percepta-test -- --yes
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
The helper expects a clean, committed git worktree because GitHub Actions builds from the pushed repo. It can create `Percepta-Core/__REPO_NAME__` if it does not exist yet, and it pushes the current branch to `main` before triggering releases.
|
|
12
|
+
The generated app owns its buildable service identity. The infra repo owns
|
|
13
|
+
customer blueprints, environment installations, app database registration,
|
|
14
|
+
runtime dependencies, ingress, secrets, and rollout policy.
|
|
32
15
|
|
|
33
16
|
## What's in these files
|
|
34
17
|
|
|
35
18
|
**`__APP_NAME__.service.yaml`** — Ryvn server service for the web app. It points at `Percepta-Core/__REPO_NAME__` and builds from `packages/__APP_NAME__/`.
|
|
36
19
|
|
|
37
|
-
**`__APP_NAME__-terraform.service.yaml`** — Ryvn Terraform service that owns the per-app Postgres schema in the shared `demos` database.
|
|
38
|
-
|
|
39
|
-
**`__APP_NAME__.env.percepta-test.serviceinstallation.yaml`** — web app installation for `percepta-test`. It wires the shared platform services, ingress, health probes, database connection, Inngest, OTEL/LGTM telemetry, the Langfuse base URL, and the shared demo variable group.
|
|
40
|
-
|
|
41
|
-
**`__APP_NAME__-terraform.env.percepta-test.serviceinstallation.yaml`** — schema installation for `percepta-test`.
|
|
42
|
-
|
|
43
|
-
**`percepta-test.secrets.env`** — generated locally and ignored by git. The deploy helper injects app-specific auth/encryption secrets into the Ryvn installation create manifest so the first pod starts with auth configured; shared Langfuse and LLM demo keys are inherited from a Ryvn variable group.
|
|
44
|
-
|
|
45
|
-
## Platform Wiring
|
|
46
|
-
|
|
47
|
-
`pnpm deploy:percepta-test` checks these existing `percepta-test` installations before it mutates GitHub or Ryvn app resources:
|
|
48
|
-
|
|
49
|
-
- `percepta-internal-terraform` for the shared Postgres instance.
|
|
50
|
-
- `inngest-test` for background jobs and function callbacks.
|
|
51
|
-
- `otel-collector` for trace, metric, and log collection.
|
|
52
|
-
- `lgtm-stack-helm` for Loki, Grafana, Tempo, and Mimir.
|
|
53
|
-
- `langfuse` for LLM tracing and eval observability.
|
|
54
|
-
- `demos-commons` variable group for shared demo configuration, including the Anthropic API key and Langfuse demo project keys.
|
|
55
|
-
|
|
56
|
-
The service installation sets `LANGFUSE_BASE_URL` to the shared `percepta-test` Langfuse URL, sets `LLM_PROVIDER=anthropic`, and attaches `demos-commons` for `ANTHROPIC_API_KEY`, `LANGFUSE_PUBLIC_KEY`, and sensitive `LANGFUSE_SECRET_KEY`. Individual demo apps do not need LLM or Langfuse keys in `percepta-test.secrets.env`.
|
|
57
|
-
|
|
58
20
|
## Notes
|
|
59
21
|
|
|
60
|
-
The release
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
## Adding More Environments
|
|
65
|
-
|
|
66
|
-
Copy the two `percepta-test` installation manifests to `environments/<env>/installations/`, then change the `environment:`, host, and environment-specific config.
|
|
22
|
+
The release workflow lives at the repo root under `.github/workflows/` after
|
|
23
|
+
scaffolding. It creates a Ryvn service release from the current app image.
|
|
24
|
+
Installing that release into any environment is a customer/stack-specific
|
|
25
|
+
infra concern and should be modeled in the infra repo.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as nextEnvModule from "@next/env";
|
|
2
|
-
import { getPgSearchPathOption } from "@percepta/database";
|
|
3
2
|
import type { Config } from "drizzle-kit";
|
|
4
3
|
import { getEnvConfig } from "./src/config/getEnvConfig";
|
|
5
4
|
|
|
@@ -9,34 +8,14 @@ const nextEnv =
|
|
|
9
8
|
|
|
10
9
|
nextEnv.loadEnvConfig(process.cwd());
|
|
11
10
|
|
|
12
|
-
const {
|
|
13
|
-
DATABASE_HOST: host,
|
|
14
|
-
DATABASE_PORT: port,
|
|
15
|
-
DATABASE_USERNAME: user,
|
|
16
|
-
DATABASE_PASSWORD: password,
|
|
17
|
-
DATABASE_NAME: database,
|
|
18
|
-
DATABASE_SCHEMA: databaseSchema,
|
|
19
|
-
DATABASE_USE_SSL: useSSL,
|
|
20
|
-
} = getEnvConfig();
|
|
21
|
-
|
|
22
|
-
const schemaFilter = databaseSchema?.trim() || undefined;
|
|
23
|
-
const searchPathOption = getPgSearchPathOption(schemaFilter);
|
|
24
|
-
const connectionParams = new URLSearchParams();
|
|
25
|
-
if (useSSL) connectionParams.set("sslmode", "require");
|
|
26
|
-
if (searchPathOption) connectionParams.set("options", searchPathOption);
|
|
27
|
-
|
|
28
|
-
const connectionString =
|
|
29
|
-
`postgresql://${encodeURIComponent(user)}:${encodeURIComponent(password)}` +
|
|
30
|
-
`@${host}:${port}/${encodeURIComponent(database)}` +
|
|
31
|
-
(connectionParams.size > 0 ? `?${connectionParams.toString()}` : "");
|
|
11
|
+
const { DATABASE_URL: databaseUrl } = getEnvConfig();
|
|
32
12
|
|
|
33
13
|
const config: Config = {
|
|
34
14
|
schema: "./src/drizzle/schema",
|
|
35
15
|
out: "./src/drizzle/migrations",
|
|
36
16
|
dialect: "postgresql",
|
|
37
|
-
...(schemaFilter ? { schemaFilter: [schemaFilter] } : {}),
|
|
38
17
|
dbCredentials: {
|
|
39
|
-
url:
|
|
18
|
+
url: databaseUrl,
|
|
40
19
|
},
|
|
41
20
|
};
|
|
42
21
|
|
|
@@ -2,21 +2,16 @@
|
|
|
2
2
|
NODE_ENV=development
|
|
3
3
|
APP_BASE_URL=http://localhost:3000
|
|
4
4
|
|
|
5
|
-
# Database
|
|
6
|
-
|
|
7
|
-
DATABASE_PORT=5434
|
|
8
|
-
DATABASE_USERNAME=postgres
|
|
9
|
-
DATABASE_PASSWORD=postgres
|
|
10
|
-
DATABASE_NAME=__DB_NAME__
|
|
11
|
-
# DATABASE_SCHEMA=
|
|
12
|
-
DATABASE_USE_SSL=false
|
|
5
|
+
# App Database
|
|
6
|
+
DATABASE_URL=postgresql://postgres:postgres@localhost:5434/__DB_NAME__
|
|
13
7
|
|
|
14
8
|
# Authentication (Better Auth)
|
|
15
9
|
BETTER_AUTH_SECRET=generate-with-openssl-rand-base64-32
|
|
16
10
|
BETTER_AUTH_URL=http://localhost:3000
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
#
|
|
11
|
+
|
|
12
|
+
# Shared Auth Database
|
|
13
|
+
# Deployed apps should set this from the customer monorepo auth database Secret.
|
|
14
|
+
# Local development can leave it empty and use the shared auth package defaults.
|
|
20
15
|
# AUTH_DATABASE_URL=
|
|
21
16
|
|
|
22
17
|
# Security
|
|
@@ -46,9 +41,8 @@ NEXT_PUBLIC_FARO_APP_ENVIRONMENT=development
|
|
|
46
41
|
# LANGFUSE_SECRET_KEY=
|
|
47
42
|
|
|
48
43
|
# LLM providers
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
# shell profile or ~/.config/percepta/create.env.
|
|
44
|
+
# For local LLM testing, set ANTHROPIC_API_KEY once in your shell profile or
|
|
45
|
+
# ~/.config/percepta/create.env.
|
|
52
46
|
# ANTHROPIC_API_KEY=
|
|
53
47
|
# OPENAI_API_KEY=
|
|
54
48
|
# LLM_PROVIDER=anthropic
|
|
@@ -11,21 +11,15 @@
|
|
|
11
11
|
"start": "next start",
|
|
12
12
|
"lint": "eslint .",
|
|
13
13
|
"setup": "pnpm --dir ../.. run setup",
|
|
14
|
-
"docker:up": "docker compose up -d --wait",
|
|
15
|
-
"docker:down": "docker compose down",
|
|
16
14
|
"access:validate": "percepta-access-control validate",
|
|
17
15
|
"access:apply-local": "pnpm --dir ../.. run access:apply-local",
|
|
18
|
-
"auth:db:
|
|
16
|
+
"auth:db:migrate": "pnpm --dir ../.. run auth:db:migrate",
|
|
19
17
|
"inngest:dev": "pnpm dlx inngest-cli@latest dev -u http://localhost:3000/api/inngest",
|
|
20
18
|
"db:generate": "percepta-db generate-migrations",
|
|
21
|
-
"db:migrate": "percepta-db migrate
|
|
22
|
-
"db:setup": "percepta-db setup
|
|
23
|
-
"db:
|
|
24
|
-
"db:setup-readonly": "percepta-db setup-readonly --database __DB_NAME__",
|
|
25
|
-
"db:studio": "pnpm db:setup-and-migrate && drizzle-kit studio",
|
|
19
|
+
"db:migrate": "percepta-db migrate",
|
|
20
|
+
"db:setup-readonly": "percepta-db setup-readonly",
|
|
21
|
+
"db:studio": "pnpm db:migrate && drizzle-kit studio",
|
|
26
22
|
"db:seed": "tsx ./scripts/seed.ts",
|
|
27
|
-
"deploy:percepta-test": "percepta-deploy percepta-test --app __APP_NAME__ --repo __REPO_NAME__",
|
|
28
|
-
"deploy:percepta-test:pr": "percepta-deploy percepta-test pr --app __APP_NAME__ --database-schema __APP_NAME_SNAKE__",
|
|
29
23
|
"test": "vitest run",
|
|
30
24
|
"test:e2e": "pnpm run setup && playwright test",
|
|
31
25
|
"test:e2e:install": "playwright install chromium",
|
|
@@ -48,13 +42,13 @@
|
|
|
48
42
|
"@mantine/hooks": "^8.3.1",
|
|
49
43
|
"@next/env": "^15.3.5",
|
|
50
44
|
"@opentelemetry/api": "^1.9.0",
|
|
51
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
52
|
-
"@opentelemetry/exporter-trace-otlp-proto": "^0.
|
|
53
|
-
"@opentelemetry/sdk-node": "^0.
|
|
45
|
+
"@opentelemetry/auto-instrumentations-node": "^0.75.0",
|
|
46
|
+
"@opentelemetry/exporter-trace-otlp-proto": "^0.217.0",
|
|
47
|
+
"@opentelemetry/sdk-node": "^0.217.0",
|
|
54
48
|
"@__REPO_NAME__/auth": "workspace:*",
|
|
55
49
|
"@percepta/access-control": "0.8.0",
|
|
56
50
|
"@percepta/ai": "0.1.0",
|
|
57
|
-
"@percepta/database": "0.1.
|
|
51
|
+
"@percepta/database": "0.1.2",
|
|
58
52
|
"@percepta/design": "0.4.0",
|
|
59
53
|
"@percepta/inngest": "0.1.0",
|
|
60
54
|
"@percepta/logger": "0.1.0",
|
|
@@ -102,7 +96,6 @@
|
|
|
102
96
|
"@next/eslint-plugin-next": "^15.3.5",
|
|
103
97
|
"@playwright/test": "^1.58.2",
|
|
104
98
|
"@percepta/build": "0.4.0",
|
|
105
|
-
"@percepta/deploy": "0.1.0",
|
|
106
99
|
"@tailwindcss/postcss": "^4.1.11",
|
|
107
100
|
"@types/formidable": "^3.4.5",
|
|
108
101
|
"@types/he": "^1.2.3",
|
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
# Check if database connection variables are set
|
|
2
|
-
if [ -z "$
|
|
3
|
-
echo "⚠️
|
|
4
|
-
echo "Required environment
|
|
5
|
-
echo "❌ Error: Missing required database environment variables."
|
|
1
|
+
# Check if app database connection variables are set
|
|
2
|
+
if [ -z "$DATABASE_URL" ]; then
|
|
3
|
+
echo "⚠️ App database connection not configured. Skipping migration."
|
|
4
|
+
echo "Required environment variable: DATABASE_URL"
|
|
5
|
+
echo "❌ Error: Missing required app database environment variables."
|
|
6
6
|
exit 1
|
|
7
7
|
else
|
|
8
|
-
echo "
|
|
9
|
-
echo " HOST: $DATABASE_HOST"
|
|
10
|
-
echo " USER: $DATABASE_USERNAME"
|
|
11
|
-
echo " DATABASE: $DATABASE_NAME"
|
|
12
|
-
echo " SSL: ${DATABASE_USE_SSL:-false}"
|
|
8
|
+
echo "App database configuration found."
|
|
13
9
|
fi
|
|
14
10
|
|
|
15
|
-
# Run database migrations only if database is configured
|
|
16
|
-
echo "Running database migrations..."
|
|
17
|
-
if pnpm db:
|
|
18
|
-
echo "✅
|
|
11
|
+
# Run app database migrations only if database is configured
|
|
12
|
+
echo "Running app database migrations..."
|
|
13
|
+
if pnpm db:migrate; then
|
|
14
|
+
echo "✅ App database migrations completed successfully"
|
|
19
15
|
else
|
|
20
|
-
echo "❌
|
|
21
|
-
echo "Check your database configuration and connectivity."
|
|
16
|
+
echo "❌ App database migration failed. App will not start."
|
|
17
|
+
echo "Check your app database configuration and connectivity."
|
|
22
18
|
exit 1
|
|
23
19
|
fi
|
|
24
20
|
|
|
@@ -10,17 +10,10 @@ export const { getEnvConfig, schema: ENV_CONFIG_SCHEMA } = createEnvConfig(
|
|
|
10
10
|
// Application:
|
|
11
11
|
APP_BASE_URL: z.string().optional(),
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
DATABASE_PORT: z.coerce.number().int().default(5434),
|
|
16
|
-
DATABASE_USERNAME: z.string().default("postgres"),
|
|
17
|
-
DATABASE_PASSWORD: z.string().default("postgres"),
|
|
18
|
-
DATABASE_NAME: z.string().default("__DB_NAME__"),
|
|
19
|
-
DATABASE_SCHEMA: z.string().optional(),
|
|
20
|
-
DATABASE_USE_SSL: z
|
|
13
|
+
// App database:
|
|
14
|
+
DATABASE_URL: z
|
|
21
15
|
.string()
|
|
22
|
-
.
|
|
23
|
-
.default(false),
|
|
16
|
+
.default("postgresql://postgres:postgres@localhost:5434/__DB_NAME__"),
|
|
24
17
|
|
|
25
18
|
// AWS:
|
|
26
19
|
AWS_REGION: z.string().default("us-east-1"),
|
|
@@ -28,6 +21,7 @@ export const { getEnvConfig, schema: ENV_CONFIG_SCHEMA } = createEnvConfig(
|
|
|
28
21
|
// Authentication (Better Auth):
|
|
29
22
|
BETTER_AUTH_SECRET: z.string().optional(),
|
|
30
23
|
BETTER_AUTH_URL: z.string().default("http://localhost:3000"),
|
|
24
|
+
AUTH_DATABASE_URL: z.string().optional(),
|
|
31
25
|
|
|
32
26
|
// Inngest:
|
|
33
27
|
INNGEST_BASE_URL: z.string().optional(),
|
|
@@ -1,30 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createPgPool, readDatabaseConfig } from "@percepta/database";
|
|
2
2
|
import { type NodePgDatabase, drizzle } from "drizzle-orm/node-postgres";
|
|
3
|
-
import { Pool } from "pg";
|
|
3
|
+
import type { Pool } from "pg";
|
|
4
4
|
import { getEnvConfig } from "../config/getEnvConfig";
|
|
5
5
|
|
|
6
6
|
export const { client, db } = createDb();
|
|
7
7
|
|
|
8
8
|
function createDb(): { client: Pool; db: NodePgDatabase } {
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
DATABASE_PASSWORD: password,
|
|
14
|
-
DATABASE_NAME: database,
|
|
15
|
-
DATABASE_SCHEMA: databaseSchema,
|
|
16
|
-
DATABASE_USE_SSL: useSSL,
|
|
17
|
-
} = getEnvConfig();
|
|
18
|
-
|
|
19
|
-
const pool = new Pool({
|
|
20
|
-
host,
|
|
21
|
-
port,
|
|
22
|
-
user,
|
|
23
|
-
password,
|
|
24
|
-
database,
|
|
25
|
-
ssl: getPgSslConfig(useSSL),
|
|
26
|
-
options: getPgSearchPathOption(databaseSchema),
|
|
27
|
-
});
|
|
9
|
+
const { DATABASE_URL: databaseUrl } = getEnvConfig();
|
|
10
|
+
const pool = createPgPool(
|
|
11
|
+
readDatabaseConfig({ env: { DATABASE_URL: databaseUrl } }),
|
|
12
|
+
);
|
|
28
13
|
|
|
29
14
|
return { client: pool, db: drizzle(pool) };
|
|
30
15
|
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { client as authClient } from "@__REPO_NAME__/auth/db";
|
|
1
2
|
import { getEnvConfig } from "./config/getEnvConfig";
|
|
2
3
|
import { client } from "./drizzle/db";
|
|
3
4
|
import { getLogger } from "./services/logger/AppLogger";
|
|
4
5
|
|
|
5
6
|
export async function checkStartup(): Promise<boolean> {
|
|
6
|
-
return
|
|
7
|
+
return (
|
|
8
|
+
validateEnvironment() &&
|
|
9
|
+
(await validateDatabaseConnection()) &&
|
|
10
|
+
(await validateAuthDatabaseConnection())
|
|
11
|
+
);
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
function validateEnvironment(): boolean {
|
|
@@ -18,15 +23,31 @@ function validateEnvironment(): boolean {
|
|
|
18
23
|
|
|
19
24
|
async function validateDatabaseConnection(): Promise<boolean> {
|
|
20
25
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const timeoutPromise = new Promise((_, reject) =>
|
|
24
|
-
setTimeout(() => reject(new Error("Database check timeout")), 5000),
|
|
25
|
-
);
|
|
26
|
-
await Promise.race([dbCheckPromise, timeoutPromise]);
|
|
26
|
+
await checkDatabaseClient(client, "Database check timeout");
|
|
27
27
|
} catch {
|
|
28
28
|
getLogger().warn(undefined, "Database connection failed");
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
31
31
|
return true;
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
async function validateAuthDatabaseConnection(): Promise<boolean> {
|
|
35
|
+
try {
|
|
36
|
+
await checkDatabaseClient(authClient, "Auth database check timeout");
|
|
37
|
+
} catch {
|
|
38
|
+
getLogger().warn(undefined, "Auth database connection failed");
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function checkDatabaseClient(
|
|
45
|
+
databaseClient: Pick<typeof client, "query">,
|
|
46
|
+
timeoutMessage: string,
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
const dbCheckPromise = databaseClient.query("SELECT 1");
|
|
49
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
50
|
+
setTimeout(() => reject(new Error(timeoutMessage)), 5000),
|
|
51
|
+
);
|
|
52
|
+
await Promise.race([dbCheckPromise, timeoutPromise]);
|
|
53
|
+
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { setupAuthDatabase } from "@percepta/auth";
|
|
2
|
-
import { getAuthDatabaseConfig } from "../src/config/database";
|
|
3
|
-
|
|
4
|
-
async function main(): Promise<void> {
|
|
5
|
-
await setupAuthDatabase({ config: getAuthDatabaseConfig() });
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
void main().catch((error) => {
|
|
9
|
-
console.error("Shared auth database setup failed:", error);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
});
|