feat: initial scaffold - backend, frontend, Prisma schema, Docker
- Prisma schema: User, Session, RepoConfig, Preview, Job, WebhookToken, NoConfigComment, AdminSettings - Backend: auth (login/logout/me/first-user setup), webhook handler with HMAC verification, EC2 service, SSH service, deploy pipeline, job queue worker, cron workers - Frontend: Login with first-user detection, Dashboard, PreviewDetail with live log streaming, Settings, Repos config, Admin panel, SetupWizard, Privacy page - Docker Compose and Dockerfile for self-hosted deployment - Uses bcryptjs for Node 24 compatibility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PreviewStatus" AS ENUM ('PROVISIONING', 'BUILDING', 'RUNNING', 'FAILED', 'STOPPED', 'IGNORED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "JobType" AS ENUM ('DEPLOY', 'STOP', 'INACTIVITY_STOP');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "JobStatus" AS ENUM ('PENDING', 'RUNNING', 'DONE', 'FAILED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"username" VARCHAR(128) NOT NULL,
|
||||
"passwordHash" VARCHAR(256) NOT NULL,
|
||||
"isAdmin" BOOLEAN NOT NULL DEFAULT false,
|
||||
"isFounder" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"giteaUsername" VARCHAR(128),
|
||||
"giteaPAT" TEXT,
|
||||
"giteaInstanceUrl" VARCHAR(512),
|
||||
"awsAccessKeyId" VARCHAR(256),
|
||||
"awsSecretAccessKey" TEXT,
|
||||
"awsRegion" VARCHAR(64),
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Session" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"hash" VARCHAR(512) NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "RepoConfig" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"repoOwner" VARCHAR(128) NOT NULL,
|
||||
"repoName" VARCHAR(128) NOT NULL,
|
||||
"isEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"giteaWebhookId" VARCHAR(128),
|
||||
"denyList" TEXT[],
|
||||
"instanceType" VARCHAR(64) NOT NULL DEFAULT 't2.medium',
|
||||
"inactivityHours" DOUBLE PRECISION NOT NULL DEFAULT 12,
|
||||
"port" INTEGER NOT NULL DEFAULT 3000,
|
||||
"envVars" JSONB NOT NULL DEFAULT '{}',
|
||||
"useDockerCompose" BOOLEAN NOT NULL DEFAULT false,
|
||||
"composeFilePath" VARCHAR(512),
|
||||
"aptPackages" TEXT[],
|
||||
"setupCommands" TEXT[],
|
||||
"buildCommands" TEXT[],
|
||||
"postBuildCommands" TEXT[],
|
||||
"runCommand" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "RepoConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Preview" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"repoConfigId" INTEGER NOT NULL,
|
||||
"prNumber" INTEGER NOT NULL,
|
||||
"prTitle" VARCHAR(512) NOT NULL,
|
||||
"commitSha" VARCHAR(64) NOT NULL,
|
||||
"instanceId" VARCHAR(64),
|
||||
"instanceIp" VARCHAR(64),
|
||||
"port" INTEGER NOT NULL DEFAULT 3000,
|
||||
"status" "PreviewStatus" NOT NULL DEFAULT 'PROVISIONING',
|
||||
"logs" TEXT NOT NULL DEFAULT '',
|
||||
"pid" INTEGER,
|
||||
"sshPrivateKey" TEXT,
|
||||
"sshKeyName" VARCHAR(128),
|
||||
"giteaCommentId" INTEGER,
|
||||
"lastActivityAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"stoppedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "Preview_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Job" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"previewId" INTEGER,
|
||||
"type" "JobType" NOT NULL,
|
||||
"status" "JobStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"payload" JSONB NOT NULL DEFAULT '{}',
|
||||
"error" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"startedAt" TIMESTAMP(3),
|
||||
"finishedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "Job_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "WebhookToken" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"token" VARCHAR(256) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "WebhookToken_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "NoConfigComment" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"repoOwner" VARCHAR(128) NOT NULL,
|
||||
"repoName" VARCHAR(128) NOT NULL,
|
||||
"prNumber" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "NoConfigComment_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AdminSettings" (
|
||||
"id" INTEGER NOT NULL DEFAULT 1,
|
||||
"defaultInstanceType" VARCHAR(64) NOT NULL DEFAULT 't2.medium',
|
||||
"maxConcurrentInstancesPerUser" INTEGER NOT NULL DEFAULT 5,
|
||||
"logSizeLimitBytes" INTEGER NOT NULL DEFAULT 1048576,
|
||||
"previewRetentionDays" INTEGER NOT NULL DEFAULT 30,
|
||||
"webhookRateLimitPerMinute" INTEGER NOT NULL DEFAULT 10,
|
||||
"contactEmail" VARCHAR(256) NOT NULL DEFAULT '',
|
||||
|
||||
CONSTRAINT "AdminSettings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "RepoConfig_repoOwner_repoName_key" ON "RepoConfig"("repoOwner", "repoName");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WebhookToken_userId_key" ON "WebhookToken"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "NoConfigComment_userId_repoOwner_repoName_prNumber_key" ON "NoConfigComment"("userId", "repoOwner", "repoName", "prNumber");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "RepoConfig" ADD CONSTRAINT "RepoConfig_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Preview" ADD CONSTRAINT "Preview_repoConfigId_fkey" FOREIGN KEY ("repoConfigId") REFERENCES "RepoConfig"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Job" ADD CONSTRAINT "Job_previewId_fkey" FOREIGN KEY ("previewId") REFERENCES "Preview"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WebhookToken" ADD CONSTRAINT "WebhookToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "NoConfigComment" ADD CONSTRAINT "NoConfigComment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
@@ -0,0 +1,160 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
enum PreviewStatus {
|
||||
PROVISIONING
|
||||
BUILDING
|
||||
RUNNING
|
||||
FAILED
|
||||
STOPPED
|
||||
IGNORED
|
||||
}
|
||||
|
||||
enum JobType {
|
||||
DEPLOY
|
||||
STOP
|
||||
INACTIVITY_STOP
|
||||
}
|
||||
|
||||
enum JobStatus {
|
||||
PENDING
|
||||
RUNNING
|
||||
DONE
|
||||
FAILED
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique @db.VarChar(128)
|
||||
passwordHash String @db.VarChar(256)
|
||||
isAdmin Boolean @default(false)
|
||||
isFounder Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
giteaUsername String? @db.VarChar(128)
|
||||
giteaPAT String? @db.Text
|
||||
giteaInstanceUrl String? @db.VarChar(512)
|
||||
|
||||
awsAccessKeyId String? @db.VarChar(256)
|
||||
awsSecretAccessKey String? @db.Text
|
||||
awsRegion String? @db.VarChar(64)
|
||||
|
||||
sessions Session[]
|
||||
repoConfigs RepoConfig[]
|
||||
webhookToken WebhookToken?
|
||||
noConfigComments NoConfigComment[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
id Int @id @default(autoincrement())
|
||||
hash String @db.VarChar(512)
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model RepoConfig {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
repoOwner String @db.VarChar(128)
|
||||
repoName String @db.VarChar(128)
|
||||
isEnabled Boolean @default(false)
|
||||
|
||||
giteaWebhookId String? @db.VarChar(128)
|
||||
|
||||
denyList String[]
|
||||
instanceType String @default("t2.medium") @db.VarChar(64)
|
||||
inactivityHours Float @default(12)
|
||||
port Int @default(3000)
|
||||
envVars Json @default("{}")
|
||||
useDockerCompose Boolean @default(false)
|
||||
composeFilePath String? @db.VarChar(512)
|
||||
aptPackages String[]
|
||||
setupCommands String[]
|
||||
buildCommands String[]
|
||||
postBuildCommands String[]
|
||||
runCommand String? @db.Text
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
previews Preview[]
|
||||
|
||||
@@unique([repoOwner, repoName])
|
||||
}
|
||||
|
||||
model Preview {
|
||||
id Int @id @default(autoincrement())
|
||||
repoConfigId Int
|
||||
repoConfig RepoConfig @relation(fields: [repoConfigId], references: [id], onDelete: Cascade)
|
||||
prNumber Int
|
||||
prTitle String @db.VarChar(512)
|
||||
commitSha String @db.VarChar(64)
|
||||
instanceId String? @db.VarChar(64)
|
||||
instanceIp String? @db.VarChar(64)
|
||||
port Int @default(3000)
|
||||
status PreviewStatus @default(PROVISIONING)
|
||||
logs String @default("") @db.Text
|
||||
pid Int?
|
||||
sshPrivateKey String? @db.Text
|
||||
sshKeyName String? @db.VarChar(128)
|
||||
giteaCommentId Int?
|
||||
lastActivityAt DateTime @default(now())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
stoppedAt DateTime?
|
||||
|
||||
jobs Job[]
|
||||
}
|
||||
|
||||
model Job {
|
||||
id Int @id @default(autoincrement())
|
||||
previewId Int?
|
||||
preview Preview? @relation(fields: [previewId], references: [id], onDelete: Cascade)
|
||||
type JobType
|
||||
status JobStatus @default(PENDING)
|
||||
payload Json @default("{}")
|
||||
error String? @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
startedAt DateTime?
|
||||
finishedAt DateTime?
|
||||
}
|
||||
|
||||
model WebhookToken {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int @unique
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
token String @db.VarChar(256)
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model NoConfigComment {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
repoOwner String @db.VarChar(128)
|
||||
repoName String @db.VarChar(128)
|
||||
prNumber Int
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([userId, repoOwner, repoName, prNumber])
|
||||
}
|
||||
|
||||
model AdminSettings {
|
||||
id Int @id @default(1)
|
||||
defaultInstanceType String @default("t2.medium") @db.VarChar(64)
|
||||
maxConcurrentInstancesPerUser Int @default(5)
|
||||
logSizeLimitBytes Int @default(1048576)
|
||||
previewRetentionDays Int @default(30)
|
||||
webhookRateLimitPerMinute Int @default(10)
|
||||
contactEmail String @default("") @db.VarChar(256)
|
||||
}
|
||||
Reference in New Issue
Block a user