docs: schema,migrations,scripts (2 files)

This commit is contained in:
Richard Ward
2025-12-30 21:37:27 +00:00
parent af2f9ad2fb
commit 43477aceae
2 changed files with 243 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
#!/bin/bash
# Docker entrypoint hook for schema migrations
# Called during container startup to apply approved migrations
set -e
REGISTRY_FILE="/app/prisma/schema-registry.json"
GENERATED_PRISMA="/app/prisma/generated-from-packages.prisma"
echo "🔍 Checking for pending schema migrations..."
# Check if registry exists
if [ ! -f "$REGISTRY_FILE" ]; then
echo "✓ No schema registry found - skipping"
exit 0
fi
# Check for approved migrations
APPROVED_COUNT=$(cat "$REGISTRY_FILE" | python3 -c "
import json, sys
data = json.load(sys.stdin)
count = sum(1 for m in data.get('migrationQueue', []) if m['status'] == 'approved')
print(count)
")
if [ "$APPROVED_COUNT" -eq 0 ]; then
echo "✓ No approved migrations pending"
exit 0
fi
echo "⚠️ Found $APPROVED_COUNT approved migration(s) to apply"
# Generate Prisma schema fragment
echo "📝 Generating Prisma schema..."
npx ts-node /app/tools/codegen/schema-cli.ts generate
# Check if generated file exists
if [ ! -f "$GENERATED_PRISMA" ]; then
echo "❌ Failed to generate Prisma schema"
exit 1
fi
echo "📋 Generated schema fragment:"
cat "$GENERATED_PRISMA"
# Apply migrations
echo ""
echo "🚀 Running Prisma migrate..."
npx prisma migrate dev --name "package-schema-$(date +%Y%m%d%H%M%S)" --skip-generate
if [ $? -eq 0 ]; then
echo "✓ Migrations applied successfully"
# Mark migrations as applied in registry
python3 -c "
import json
with open('$REGISTRY_FILE', 'r') as f:
data = json.load(f)
import time
for m in data.get('migrationQueue', []):
if m['status'] == 'approved':
m['status'] = 'applied'
m['appliedAt'] = int(time.time() * 1000)
# Update entity registry
data['entities'][m['entityName']] = {
'checksum': m['newChecksum'],
'version': '1.0',
'ownerPackage': m['packageId'],
'prismaModel': m['prismaPreview'],
'appliedAt': m['appliedAt']
}
with open('$REGISTRY_FILE', 'w') as f:
json.dump(data, f, indent=2)
print('✓ Registry updated')
"
# Regenerate Prisma client
echo "🔧 Regenerating Prisma client..."
npx prisma generate
echo ""
echo "✅ Schema migrations complete!"
else
echo "❌ Migration failed!"
exit 1
fi

View File

@@ -0,0 +1,153 @@
# Package Schema Migration System
## Overview
Packages can define their own database schemas in `seed/schema/entities.yaml`. The system:
1. **Validates** schemas across packages to prevent conflicts
2. **Queues** changes for admin approval
3. **Generates** Prisma schema fragments
4. **Applies** migrations on container restart
## Flow
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Package with │────▶│ npm run │────▶│ Admin reviews │
│ schema/ │ │ schema:scan │ │ pending queue │
│ entities.yaml │ │ │ │ │
└─────────────────┘ └──────────────────┘ └────────┬────────┘
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Prisma migrate │◀────│ Container │◀────│ Admin approves │
│ applied │ │ restart │ │ migrations │
└─────────────────┘ └──────────────────┘ └─────────────────┘
```
## Package Schema Format
Create `packages/{name}/seed/schema/entities.yaml`:
```yaml
entities:
- name: MyEntity
version: "1.0"
description: "Description of entity"
fields:
id:
type: cuid
primary: true
generated: true
tenantId:
type: string
required: true
index: true
name:
type: string
required: true
maxLength: 100
data:
type: string
nullable: true
description: "JSON: additional data"
createdAt:
type: bigint
required: true
indexes:
- fields: [tenantId, name]
unique: true
relations:
- name: tenant
type: belongsTo
entity: Tenant
field: tenantId
onDelete: Cascade
acl:
create: [user]
read: [public]
update: [self, admin]
delete: [admin]
```
## Field Types
| Type | Prisma | Notes |
|------|--------|-------|
| `string` | `String` | Text fields |
| `int` | `Int` | 32-bit integer |
| `bigint` | `BigInt` | 64-bit integer (timestamps) |
| `float` | `Float` | Decimal numbers |
| `boolean` | `Boolean` | True/false |
| `cuid` | `String` | CUID identifier |
| `uuid` | `String` | UUID identifier |
| `json` | `Json` | JSON data |
## CLI Commands
```bash
# Scan all packages for schema changes
npm run schema:scan
# List pending migrations
npm run schema:list
# Approve migrations
npm run schema:approve all
npm run schema:approve <migration-id>
# Generate Prisma fragment
npm run schema:generate
# Preview package schema as Prisma
npm run schema:preview audit_log
# Check status
npm run schema:status
```
## Checksum Validation
The system computes checksums from schema structure (not descriptions). If two packages define the same entity:
- **Same checksum**: Compatible, no conflict
- **Different checksum**: Error - packages must coordinate
## Metadata.json Integration
Add schema export to `metadata.json`:
```json
{
"packageId": "my_package",
"schema": {
"entities": ["MyEntity", "MyOtherEntity"],
"path": "schema/entities.yaml"
}
}
```
## Docker Integration
The container startup script (`apply-schema-migrations.sh`) automatically:
1. Checks for approved migrations
2. Generates Prisma schema fragment
3. Runs `prisma migrate dev`
4. Updates registry to mark as applied
5. Regenerates Prisma client
## Security
- Only **admin** can approve migrations
- Schema changes require container restart
- Checksums prevent unauthorized modifications
- ACL rules define entity-level permissions