feat(deploy): switch to Oracle MySQL, separate CI runner, docker-compose deploy

- Prisma schema: sqlite → mysql provider
- Migration SQL rewritten as MySQL DDL (utf8mb4, DATETIME(3), AUTO_INCREMENT)
- Add migration_lock.toml for mysql provider
- Dockerfile.backend: drop node:22/sqlite deps, use node:20-slim
- entrypoint.sh: replace SQLite first-run logic with prisma migrate deploy + db seed
- docker-compose.prod.yml: production compose for /opt/webapps/okr on web VPS
  - reads DB creds from /opt/webapps/webapp-mysql.env
  - reads app secrets from /opt/webapps/okr/.env.app (written by CI)
  - port 80 (frontend), no conflict with Gitea 3000/Vault 8200
- ci.yml deploy-okr: moves from ubuntu-latest (web VPS) to ci-runner (161.33.149.243)
  - builds images on CI runner VPS (no heavy build on web/Gitea VPS)
  - transfers images via docker save | gzip | ssh | docker load
  - deploys via SSH + docker compose up on web VPS
- scripts/setup-ci-runner.sh: one-time setup script for CI runner VPS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
thanhnv
2026-07-01 16:37:56 +09:00
co-authored by Claude Sonnet 4.6
parent 719f1147d1
commit 9229fee656
8 changed files with 284 additions and 122 deletions
@@ -1,53 +1,65 @@
-- Initial OKR SQLite schema generated from prisma/schema.prisma via prisma migrate diff.
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"role" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- MySQL initial schema for OKR application
CREATE TABLE `User` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`username` VARCHAR(191) NOT NULL,
`email` VARCHAR(191) NOT NULL,
`passwordHash` VARCHAR(191) NOT NULL,
`role` VARCHAR(191) NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `User_username_key`(`username`),
UNIQUE INDEX `User_email_key`(`email`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE "Objective" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"description" TEXT,
"ownerId" INTEGER NOT NULL,
"quarter" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'NOT_STARTED',
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Objective_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE `Objective` (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(191) NOT NULL,
`description` VARCHAR(191) NULL,
`ownerId` INT NOT NULL,
`quarter` VARCHAR(191) NOT NULL,
`status` VARCHAR(191) NOT NULL DEFAULT 'NOT_STARTED',
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
INDEX `Objective_ownerId_idx`(`ownerId`),
INDEX `Objective_quarter_idx`(`quarter`),
INDEX `Objective_status_idx`(`status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE "KeyResult" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"objectiveId" INTEGER NOT NULL,
"title" TEXT NOT NULL,
"progress" INTEGER NOT NULL DEFAULT 0,
"startValue" INTEGER NOT NULL,
"targetValue" INTEGER NOT NULL,
"deadline" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "KeyResult_objectiveId_fkey" FOREIGN KEY ("objectiveId") REFERENCES "Objective" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE `KeyResult` (
`id` INT NOT NULL AUTO_INCREMENT,
`objectiveId` INT NOT NULL,
`title` VARCHAR(191) NOT NULL,
`progress` INT NOT NULL DEFAULT 0,
`startValue` INT NOT NULL,
`targetValue` INT NOT NULL,
`deadline` DATETIME(3) NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
INDEX `KeyResult_objectiveId_idx`(`objectiveId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE "ProgressUpdate" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"keyResultId" INTEGER NOT NULL,
"progress" INTEGER NOT NULL,
"comment" TEXT,
"createdById" INTEGER NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ProgressUpdate_keyResultId_fkey" FOREIGN KEY ("keyResultId") REFERENCES "KeyResult" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE `ProgressUpdate` (
`id` INT NOT NULL AUTO_INCREMENT,
`keyResultId` INT NOT NULL,
`progress` INT NOT NULL,
`comment` VARCHAR(191) NULL,
`createdById` INT NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
CREATE INDEX "Objective_ownerId_idx" ON "Objective"("ownerId");
CREATE INDEX "Objective_quarter_idx" ON "Objective"("quarter");
CREATE INDEX "Objective_status_idx" ON "Objective"("status");
CREATE INDEX "KeyResult_objectiveId_idx" ON "KeyResult"("objectiveId");
ALTER TABLE `Objective` ADD CONSTRAINT `Objective_ownerId_fkey`
FOREIGN KEY (`ownerId`) REFERENCES `User`(`id`)
ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `KeyResult` ADD CONSTRAINT `KeyResult_objectiveId_fkey`
FOREIGN KEY (`objectiveId`) REFERENCES `Objective`(`id`)
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `ProgressUpdate` ADD CONSTRAINT `ProgressUpdate_keyResultId_fkey`
FOREIGN KEY (`keyResultId`) REFERENCES `KeyResult`(`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 = "mysql"