4413045f-d05e-4d7a-a70d-81d6be253c85

This commit is contained in:
Z User committed 2026-10-28 16:37:26 +00:00
1 parent 762f3d7373
commit c6adc67479
18 files changed
+5263 -1

No files matched your search

+92
View File
@@ -105,6 +105,7 @@ model Company {
expenses Expense[]
tickets Ticket[]
agencyApplication AgencyApplication?
instances Instance[]
}
model Person {
@@ -291,6 +292,10 @@ model License {
stripeSubscriptionId String?
stripePriceId String?
// Link to the tenant instance (1:1). Set when this license has a deployed
// instance on the Dokploy server.
instance Instance?
createdAt DateTime @default(now())
activations LicenseActivation[]
}
@@ -892,3 +897,90 @@ model CustomFieldValue {
@@unique([entityType, entityId, fieldDefId])
}
// ---------------------------------------------------------------------------
// Tenant Instance Provisioning (PRD §6: SaaS demo + paid instances via Dokploy)
// ---------------------------------------------------------------------------
// AOS runs at dfmpanel.eu. Each license may have a tenant instance: an isolated
// deployment of restov1 (or another product) running on the Dokploy server,
// reachable under <random>.dfmpanel.eu (demo) or the customer's own domain.
//
// Lifecycle:
// requested -> provisioning -> demo_active --(subscribe)--> active
// | |
// (expired/cancelled) (sub cancelled)
// v v
// grace <-------------------+
// |
// (grace elapsed)
// v
// deleting -> deleted
// failed (retried, then alerts operator)
model Instance {
id String @id @default(cuid())
licenseId String? @unique // link to License (1:1, nullable for orphan cleanup)
license License? @relation(fields: [licenseId], references: [id])
companyId String // owning customer
company Company @relation(fields: [companyId], references: [id])
product String // 'restov1', 'aos-cms', ... (which template to deploy)
status String @default("requested") // requested|provisioning|demo_active|active|grace|deleting|deleted|failed
plan String @default("pro") // starter|pro|enterprise (resource limits)
// Subdomain on the platform domain (e.g. "k7f3a9" -> k7f3a9.dfmpanel.eu)
subdomain String @unique
// Dokploy server id (for future multi-server; v1 single server)
serverId String @default("default")
// Lifecycle timestamps
demoExpiresAt DateTime?
graceEndsAt DateTime?
deletedAt DateTime?
// Custom domain (customer's own domain, verified via TXT)
customDomain String?
domainVerificationToken String?
domainVerifiedAt DateTime?
// Dokploy resource IDs (returned by Dokploy API on creation)
dokployAppId String?
dokployDbId String?
dokployDbName String?
dokployDbUser String?
dokployDbPassword String? // stored encrypted in production; demo ok plaintext dev
// Hetzner DNS record ID (null if using wildcard DNS)
dnsRecordId String?
// Admin login link (one-time, demo only)
adminLoginToken String?
adminLoginExpiresAt DateTime?
// Provisioning log + metadata
metadata String? // JSON: { gitRepo, gitBranch, imageTag, resources }
lastError String?
lastErrorAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
events InstanceEvent[]
jobs ProvisioningJob[]
}
model InstanceEvent {
id String @id @default(cuid())
instanceId String
instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
type String // provision.start, provision.dokploy.created, provision.dns.created, provision.ok, delete.start, delete.dokploy.removed, delete.ok, suspend, reactivate, domain.verify, domain.attached, etc.
step String? // sub-step identifier
result String // ok|error|pending
details String? // JSON
createdAt DateTime @default(now())
}
model ProvisioningJob {
id String @id @default(cuid())
instanceId String
instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
kind String // provision|delete|domain|suspend|reactivate
step String // current step in the sequence
attempts Int @default(0)
maxAttempts Int @default(5)
lastError String?
status String @default("pending") // pending|running|done|failed
nextRunAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}