Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"node-cron": "^3.0.3",
"redis": "^4.7.0",
"socket.io": "^4.8.1",
"zod": "^3.24.2"
Expand All @@ -38,6 +39,7 @@
"@types/morgan": "^1.9.9",
"@types/multer": "^1.4.12",
"@types/node": "^22.13.2",
"@types/node-cron": "^3.0.11",
"@types/redis": "^4.0.10",
"prisma": "^6.3.1",
"ts-node": "^10.9.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ CREATE TABLE "FailedJob" (
CONSTRAINT "FailedJob_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Image" (
"id" TEXT NOT NULL,
"key" TEXT NOT NULL,
"url" TEXT NOT NULL,
"mimeType" TEXT,
"size" INTEGER,
"ownerId" TEXT,
"relatedTo" TEXT,
"relatedId" TEXT,
"table" TEXT,
"hasUpload" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
"dataLogs" JSONB,

CONSTRAINT "Image_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

Expand All @@ -146,6 +166,15 @@ CREATE UNIQUE INDEX "WhitelistUser_userId_eventId_key" ON "WhitelistUser"("userI
-- CreateIndex
CREATE UNIQUE INDEX "FailedJob_jobId_key" ON "FailedJob"("jobId");

-- CreateIndex
CREATE UNIQUE INDEX "Image_key_key" ON "Image"("key");

-- CreateIndex
CREATE INDEX "Image_relatedTo_relatedId_idx" ON "Image"("relatedTo", "relatedId");

-- CreateIndex
CREATE UNIQUE INDEX "Image_relatedTo_relatedId_key_key" ON "Image"("relatedTo", "relatedId", "key");

-- AddForeignKey
ALTER TABLE "Event" ADD CONSTRAINT "Event_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

Expand Down Expand Up @@ -190,3 +219,6 @@ ALTER TABLE "Vote" ADD CONSTRAINT "Vote_userId_fkey" FOREIGN KEY ("userId") REFE

-- AddForeignKey
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_guestId_fkey" FOREIGN KEY ("guestId") REFERENCES "Guest"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Image" ADD CONSTRAINT "Image_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
148 changes: 85 additions & 63 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -8,145 +8,167 @@ datasource db {
}

model User {
id String @id @default(uuid())
firstName String
lastName String
email String @unique
avatar String?
events Event[] @relation("EventOwner")
polls Poll[] @relation("PollOwner")
whitelist WhitelistUser[]
votes Vote[]
userVotes VoteRestriction[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?
id String @id @default(uuid())
firstName String
lastName String
email String @unique
avatar String?
events Event[] @relation("EventOwner")
polls Poll[] @relation("PollOwner")
whitelist WhitelistUser[]
votes Vote[]
userVotes VoteRestriction[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
Image Image[]
dataLogs Json?
}

model Event {
id String @id @default(uuid())
id String @id @default(uuid())
name String
description String?
userId String
owner User @relation("EventOwner", fields: [userId], references: [id], onDelete: Cascade)
owner User @relation("EventOwner", fields: [userId], references: [id], onDelete: Cascade)
polls Poll[]
whitelist WhitelistUser[]
guests Guest[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?
}

model Poll {
id String @id @default(uuid())
eventId String?
event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull)
id String @id @default(uuid())
eventId String?
event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull)
userId String
owner User @relation("PollOwner", fields: [userId], references: [id], onDelete: Cascade)
owner User @relation("PollOwner", fields: [userId], references: [id], onDelete: Cascade)
question String
description String?
isPublic Boolean @default(false)
canEdit Boolean @default(false)
showResult Boolean @default(false)
isPublic Boolean @default(false)
canEdit Boolean @default(false)
showResult Boolean @default(false)
startVoteAt DateTime
endVoteAt DateTime
isVoteEnd Boolean @default(false)
isVoteEnd Boolean @default(false)
banner String?
publishedAt DateTime?
options Option[]
votes Vote[]
voteRestrict VoteRestriction[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?
}

model Option {
id String @id @default(uuid())
id String @id @default(uuid())
text String
banner String?
description String?
pollId String
poll Poll @relation(fields: [pollId], references: [id], onDelete: Cascade)
poll Poll @relation(fields: [pollId], references: [id], onDelete: Cascade)
votes Vote[]
voteRestrict VoteRestriction[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?
}

model WhitelistUser {
id String @id @default(uuid())
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
eventId String?
event Event? @relation(fields: [eventId], references: [id], onDelete: Cascade)
point Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event Event? @relation(fields: [eventId], references: [id], onDelete: Cascade)
point Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?

@@unique([userId, eventId])
}

model Guest {
id String @id @default(uuid())
id String @id @default(uuid())
name String
key String
point Int @default(0)
point Int @default(0)
eventId String?
event Event? @relation(fields: [eventId], references: [id], onDelete: Cascade)
event Event? @relation(fields: [eventId], references: [id], onDelete: Cascade)
votes Vote[]
guestVotes VoteRestriction[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?
}

model VoteRestriction {
id String @id @default(uuid())
userId String?
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
guestId String?
guest Guest? @relation(fields: [guestId], references: [id], onDelete: Cascade)
id String @id @default(uuid())
userId String?
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
guestId String?
guest Guest? @relation(fields: [guestId], references: [id], onDelete: Cascade)
pollId String
poll Poll @relation(fields: [pollId], references: [id], onDelete: Cascade)
poll Poll @relation(fields: [pollId], references: [id], onDelete: Cascade)
optionId String
option Option @relation(fields: [optionId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
option Option @relation(fields: [optionId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?
}

model Vote {
id String @id @default(uuid())
id String @id @default(uuid())
pollId String
poll Poll @relation(fields: [pollId], references: [id], onDelete: Cascade)
point Int @default(0)
poll Poll @relation(fields: [pollId], references: [id], onDelete: Cascade)
point Int @default(0)
optionId String
option Option @relation(fields: [optionId], references: [id], onDelete: Cascade)
userId String?
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
option Option @relation(fields: [optionId], references: [id], onDelete: Cascade)
userId String?
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
guestId String?
guest Guest? @relation(fields: [guestId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
guest Guest? @relation(fields: [guestId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?
}

model FailedJob {
id String @id @default(uuid())
jobId String @unique
id String @id @default(uuid())
jobId String @unique
queueName String
data Json
error String
createdAt DateTime @default(now())
}

model Image {
id String @id @default(uuid())
key String @unique
url String
mimeType String?
size Int?
ownerId String?
owner User? @relation(fields: [ownerId], references: [id], onDelete: Cascade)
relatedTo String?
relatedId String?
table String?
hasUpload Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
dataLogs Json?

@@unique([relatedTo, relatedId, key])
@@index([relatedTo, relatedId])
}
13 changes: 13 additions & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { envConfig } from "./config/config";
import http from "http";
import { socketService } from "./services/socket.service";
import { QueueService } from "./services/queue.service";
import { deleteUnusedImagesCron } from "./crons/deleteUnusedImages";
import cron from "node-cron";

// โหลด Environment Variables
dotenv.config();
Expand Down Expand Up @@ -72,6 +74,17 @@ const gracefulShutdown = async () => {
process.on("SIGINT", gracefulShutdown);
process.on("SIGTERM", gracefulShutdown);


cron.schedule("0 0 * * *", async () => {
console.log("⏳ Running scheduled task: Delete unused images...");
await deleteUnusedImagesCron();
});

// Start the cron job
deleteUnusedImagesCron();

console.log("✅ Cron jobs initialized.");

// เริ่มต้น Prisma และ Queue Service
startPrisma().then(() => {
const queueService = new QueueService(prisma);
Expand Down
1 change: 1 addition & 0 deletions backend/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ export const envConfig = {
cloudflareR2AccessKey: requireEnv('CLOUDFLARE_R2_ACCESS_KEY'),
cloudflareR2AccountId: requireEnv('CLOUDFLARE_R2_ACCOUNT_ID'),
cloudflareR2BucketName: requireEnv('CLOUDFLARE_R2_BUCKET_NAME'),
cloudflareR2Url: requireEnv('CLOUDFLARE_R2_URL'),
};
Loading