From 79346f47cdaa62873a3b7b2bb2b720001be0011b Mon Sep 17 00:00:00 2001 From: Mathijs Rutgers Date: Fri, 28 Feb 2025 21:16:09 +0100 Subject: [PATCH 1/3] Add seasons, games and teams --- docker-compose.yml | 4 +- .../migration.sql | 148 ++++++++++++++++++ prisma/schema.prisma | 113 +++++++++++-- 3 files changed, 254 insertions(+), 11 deletions(-) create mode 100644 prisma/migrations/20250228161049_add_teams_and_game_tracking/migration.sql diff --git a/docker-compose.yml b/docker-compose.yml index 94777e7..e12209c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,12 +11,12 @@ services: depends_on: db: condition: service_healthy - restart: on_failure + restart: always ports: - 3000:3000 db: image: mysql:latest - restart: on_failure + restart: always ports: - ${MYSQL_PORT}:${MYSQL_PORT} expose: diff --git a/prisma/migrations/20250228161049_add_teams_and_game_tracking/migration.sql b/prisma/migrations/20250228161049_add_teams_and_game_tracking/migration.sql new file mode 100644 index 0000000..7029070 --- /dev/null +++ b/prisma/migrations/20250228161049_add_teams_and_game_tracking/migration.sql @@ -0,0 +1,148 @@ +/* + Warnings: + + - You are about to drop the column `level` on the `User` table. All the data in the column will be lost. + - Added the required column `game_id` to the `Match` table without a default value. This is not possible if the table is not empty. + - Added the required column `updated_at` to the `Match` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE `Match` DROP FOREIGN KEY `Match_challenger_id_fkey`; + +-- DropForeignKey +ALTER TABLE `Match` DROP FOREIGN KEY `Match_defender_id_fkey`; + +-- AlterTable +ALTER TABLE `Match` ADD COLUMN `deleted_at` DATETIME(3) NULL, + ADD COLUMN `game_id` INTEGER NOT NULL, + ADD COLUMN `updated_at` DATETIME(3) NOT NULL, + MODIFY `played_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3); + +-- AlterTable +ALTER TABLE `User` DROP COLUMN `level`; + +-- CreateTable +CREATE TABLE `Game` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updated_at` DATETIME(3) NOT NULL, + `deleted_at` DATETIME(3) NULL, + `min_team_size` INTEGER NOT NULL, + `max_team_size` INTEGER NOT NULL, + + UNIQUE INDEX `Game_name_key`(`name`), + INDEX `Game_name_idx`(`name`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `UserGameStats` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `user_id` INTEGER NOT NULL, + `game_id` INTEGER NOT NULL, + `level` INTEGER NOT NULL, + `created_at` DATETIME(3) NOT NULL, + `updated_at` DATETIME(3) NOT NULL, + `deleted_at` DATETIME(3) NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Team` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `game_id` INTEGER NOT NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updated_at` DATETIME(3) NOT NULL, + `deleted_at` DATETIME(3) NULL, + + INDEX `Team_name_idx`(`name`), + INDEX `Team_game_id_idx`(`game_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `TeamMember` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `team_id` INTEGER NOT NULL, + `user_id` INTEGER NOT NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updated_at` DATETIME(3) NOT NULL, + `deleted_at` DATETIME(3) NULL, + + INDEX `TeamMember_team_id_idx`(`team_id`), + INDEX `TeamMember_user_id_idx`(`user_id`), + UNIQUE INDEX `TeamMember_team_id_user_id_key`(`team_id`, `user_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Rules` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `game_id` INTEGER NOT NULL, + `path` VARCHAR(255) NOT NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updated_at` DATETIME(3) NOT NULL, + `deleted_at` DATETIME(3) NULL, + + INDEX `Rules_game_id_idx`(`game_id`), + UNIQUE INDEX `Rules_game_id_key`(`game_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Season` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(191) NOT NULL, + `game_id` INTEGER NOT NULL, + `start_date` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `end_date` DATETIME(3) NULL, + `active` BOOLEAN NOT NULL DEFAULT true, + `deleted_at` DATETIME(3) NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updated_at` DATETIME(3) NOT NULL, + + INDEX `Season_game_id_idx`(`game_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateIndex +CREATE INDEX `Match_game_id_idx` ON `Match`(`game_id`); + +-- AddForeignKey +ALTER TABLE `UserGameStats` ADD CONSTRAINT `UserGameStats_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `UserGameStats` ADD CONSTRAINT `UserGameStats_game_id_fkey` FOREIGN KEY (`game_id`) REFERENCES `Game`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Match` ADD CONSTRAINT `Match_game_id_fkey` FOREIGN KEY (`game_id`) REFERENCES `Game`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Match` ADD CONSTRAINT `Match_challenger_id_fkey` FOREIGN KEY (`challenger_id`) REFERENCES `Team`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Match` ADD CONSTRAINT `Match_defender_id_fkey` FOREIGN KEY (`defender_id`) REFERENCES `Team`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Team` ADD CONSTRAINT `Team_game_id_fkey` FOREIGN KEY (`game_id`) REFERENCES `Game`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `TeamMember` ADD CONSTRAINT `TeamMember_team_id_fkey` FOREIGN KEY (`team_id`) REFERENCES `Team`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `TeamMember` ADD CONSTRAINT `TeamMember_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Rules` ADD CONSTRAINT `Rules_game_id_fkey` FOREIGN KEY (`game_id`) REFERENCES `Game`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Season` ADD CONSTRAINT `Season_game_id_fkey` FOREIGN KEY (`game_id`) REFERENCES `Game`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- RenameIndex +ALTER TABLE `Match` RENAME INDEX `Match_challenger_id_fkey` TO `Match_challenger_id_idx`; + +-- RenameIndex +ALTER TABLE `Match` RENAME INDEX `Match_defender_id_fkey` TO `Match_defender_id_idx`; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 69ca45a..a39ce8d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,20 +14,115 @@ datasource db { } model User { - id Int @id @default(autoincrement()) - name String @db.VarChar(255) - level Int - challenger_matches Match[] @relation("challenger") - defender_matches Match[] @relation("defender") + id Int @id @default(autoincrement()) + name String @db.VarChar(255) + game_stats UserGameStats[] + teams TeamMember[] +} + +model Game { + id Int @id @default(autoincrement()) + name String @unique @db.VarChar(255) + matches Match[] + seasons Season[] + teams Team[] + rules Rules? + user_stats UserGameStats[] + created_at DateTime @default(now()) @db.DateTime(3) + updated_at DateTime @updatedAt @db.DateTime(3) + deleted_at DateTime? @db.DateTime(3) + min_team_size Int + max_team_size Int + + @@index([name]) +} + +model UserGameStats { + id Int @id @default(autoincrement()) + user_id Int + user User @relation(fields: [user_id], references: [id]) + game_id Int + game Game @relation(fields: [game_id], references: [id]) + level Int + created_at DateTime @db.DateTime(3) + updated_at DateTime @updatedAt @db.DateTime(3) + deleted_at DateTime? @db.DateTime(3) } model Match { - id Int @id @default(autoincrement()) - challenger User @relation("challenger", fields: [challenger_id], references: [id]) - defender User @relation("defender", fields: [defender_id], references: [id]) + id Int @id @default(autoincrement()) + game_id Int + game Game @relation(fields: [game_id], references: [id]) challenger_id Int + challenger Team @relation("challenger", fields: [challenger_id], references: [id]) defender_id Int + defender Team @relation("defender", fields: [defender_id], references: [id]) score_challenger Int score_defender Int - played_at DateTime + played_at DateTime @default(now()) + updated_at DateTime @updatedAt @db.DateTime(3) + deleted_at DateTime? @db.DateTime(3) + + @@index([game_id]) + @@index([challenger_id]) + @@index([defender_id]) +} + +model Team { + id Int @id @default(autoincrement()) + name String @db.VarChar(255) + members TeamMember[] + matches_as_challenger Match[] @relation("challenger") + matches_as_defender Match[] @relation("defender") + game_id Int + game Game @relation(fields: [game_id], references: [id]) + created_at DateTime @default(now()) @db.DateTime(3) + updated_at DateTime @updatedAt @db.DateTime(3) + deleted_at DateTime? @db.DateTime(3) + + @@index([name]) + @@index([game_id]) +} + +model TeamMember { + id Int @id @default(autoincrement()) + team_id Int + team Team @relation(fields: [team_id], references: [id]) + user_id Int + user User @relation(fields: [user_id], references: [id]) + created_at DateTime @default(now()) @db.DateTime(3) + updated_at DateTime @updatedAt @db.DateTime(3) + deleted_at DateTime? @db.DateTime(3) + + @@unique([team_id, user_id]) + @@index([team_id]) + @@index([user_id]) +} + +model Rules { + id Int @id @default(autoincrement()) + game_id Int + game Game @relation(fields: [game_id], references: [id]) + path String @db.VarChar(255) + created_at DateTime @default(now()) @db.DateTime(3) + updated_at DateTime @updatedAt @db.DateTime(3) + deleted_at DateTime? @db.DateTime(3) + + @@unique([game_id]) + @@index([game_id]) +} + +model Season { + id Int @id @default(autoincrement()) + name String + game_id Int + game Game @relation(fields: [game_id], references: [id]) + start_date DateTime @default(now()) @db.DateTime(3) + end_date DateTime? @db.DateTime(3) + active Boolean @default(true) + deleted_at DateTime? @db.DateTime(3) + created_at DateTime @default(now()) @db.DateTime(3) + updated_at DateTime @updatedAt @db.DateTime(3) + + @@index([game_id]) } From a4b812416dd388f92e33220bb088f09d8efa822f Mon Sep 17 00:00:00 2001 From: Mathijs Rutgers Date: Fri, 28 Feb 2025 21:20:37 +0100 Subject: [PATCH 2/3] Add games seeder --- bun.lockb | Bin 225910 -> 225910 bytes package.json | 6 +++++- prisma/seeds/games.ts | 38 ++++++++++++++++++++++++++++++++++++++ prisma/seeds/main.ts | 27 +++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 prisma/seeds/games.ts create mode 100644 prisma/seeds/main.ts diff --git a/bun.lockb b/bun.lockb index 02432d1f663d224cfafc97df2120e656dc573444..d9785a4abab4fdcbd49e26ee923f743e07c01208 100755 GIT binary patch delta 1463 zcmXZcXG|196u|M>ITS^K(50xzop79yM1n>%QL%in1Uxa(XarOgYcy(X(X)UeHb7*o zAQ({*8=Ra{EPMb9n2?Ay5es5VY*D^g|7&)WU*^qwQ+9VY+tuOO)#14%Q*+9S{^{qX z_`Az*;@v@-uQhiIwsRmwo{U-TqfZ zfKlp2w@}50%P3o?z71C&qvS<=-^c|UId5`RQssop$lwrTmoTH|OD&7Ihq@Ck6GwQ` z9blA#h##<&zGQ@v>!ZiU5@sV8Om3ER*$2pc&Fc@HU-*qZZt7En3~>x&LL&Vnl%N=g zu@@&$iX$k(W^6$owju||QGjFEhD>BZ;V5<>A3L!d+i?KdC=|jWb`hL}0|&7Ohp-R( zk&AMk<}A)ZKiUh>kMj)lBdowi#w|cP7Go(EA_L2?1dEW0d6!Mk!9BiudEh71W>> zb*M)J8qtK?$|q8q9p2ov4}9T|shEaj%*Gs~VHMl1#s;j#I;_VU*fACpFb?A}8iOzt z!;ql~F&R@}M>3{sLWM@j1XU0zS2#|wOC?U=D4Lnof*ZJr>v)VVyuwRdz(rib7nI{1 z-tr>e;R8OR6>T_$GTcG~>fypwlL!^p2x?G^Iyg~{cB#CqGEg(CmMCeC2;tslu*Xa+ zB3_DRSdJ0oh6`0{l`$&WDwFy~^O*NZ-a;+Dq8kIhuEbG8K%bDq}4-XrJt%L?8COQF1edv~PvoD$LTx^^ eayQ|M8YH{?-L)ZcF|jdmJ1t|R%QD7fkNpQjY1%*l delta 1495 zcmXZcX;4jJ9LMo)T$+E-bPn9a;w^!=3~^llbal5y1y{{q)<~=(AVg5C-+g?%6fkhF+xobFuJ_R z_gP9!4>W4t_7^3A{A-ag>>x39%LXBd$CU~lSRlz4swx)d`Rra0USgD4&gA4pb$q1c_LRWk^CYreG?@ViZQg z6TRUjgH0krPm+&KB1-(#WV%UM!^`;(bCC!2dPwX?1~SpY%m;XgcD%=T{6HC+@l9s+ z7hbm0#5ZU~8(!flp5ZwjqYigag=(BZF(TP&8mx%Ibj(CFW@8TKVjf};i+EJ>&sBlf z*G0QYxP?0DY!*Q=?)0ly_%0gIh$fUXqyih+cOBMa1J+_S*1(E!7>@}E$7l@2FobHd z-7HqgT(gL_6|m7ooWM~$Vn7EzqXeaRi5F-@pov)do-g3wYZ6D zj9~0=O>VXbtDJ5T1KoplP5VNz3AL!yq*JiScJ9Q2pP2Ajey|8%5vIvw!NTe{h~Yu_ z%Fv6rgmPSlU6bYz;U~L>h`w?{h_J}@B%97kKUwKE!pRdc36oJrO?~Am$or6m9Lzu* zu5)`gP>&+U9*6o~g)yg){v$Yy11O}g4eA3`gEYns(KTPKn9d?x;2J;D@dOL$H0k!h VQ1P=@XR>OkrZK{98slIc{127~*+&2X diff --git a/package.json b/package.json index cb7b8da..eb7b070 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,11 @@ "build": "next build", "start": "next start", "lint": "next lint", - "prettier:check": "prettier --check ." + "prettier:check": "prettier --check .", + "seed": "bun run prisma/seeds/main.ts" + }, + "prisma": { + "seed": "bun seed" }, "dependencies": { "@mdx-js/react": "3.0.1", diff --git a/prisma/seeds/games.ts b/prisma/seeds/games.ts new file mode 100644 index 0000000..adadaa7 --- /dev/null +++ b/prisma/seeds/games.ts @@ -0,0 +1,38 @@ +import { Game } from '@prisma/client'; +import { PrismaClient } from '@prisma/client/extension'; + +export function seedGames(prisma: PrismaClient): Record> { + return { + foosball: prisma.game.create({ + data: { + name: 'Foosball', + min_team_size: 1, + max_team_size: 2, + seasons: { + create: { + name: `Foosball Season 1`, + start_date: new Date().toISOString(), + end_date: new Date( + new Date().setMonth(new Date().getMonth() + 3), + ).toISOString(), + active: true, + }, + }, + }, + }), + fifa: prisma.game.create({ + data: { + name: 'FIFA', + min_team_size: 1, + max_team_size: 2, + seasons: { + create: { + name: `FIFA Season 1`, + start_date: new Date().toISOString(), + active: true, + }, + }, + }, + }), + }; +} diff --git a/prisma/seeds/main.ts b/prisma/seeds/main.ts new file mode 100644 index 0000000..f3aee38 --- /dev/null +++ b/prisma/seeds/main.ts @@ -0,0 +1,27 @@ +import { PrismaClient } from '@prisma/client'; +import { seedGames } from './games'; + +async function main() { + const prisma = new PrismaClient(); + + try { + console.log('๐Ÿงผ Cleaning up...'); + + await prisma.$transaction([ + prisma.season.deleteMany(), + prisma.game.deleteMany(), + ]); + + console.log('๐ŸŒฑ Seeding games...'); + await Promise.all(Object.values(seedGames(prisma))); + + console.log('โœ… Seeding complete!'); + } catch (error) { + console.error('โŒ Error seeding data:', error); + throw error; + } finally { + await prisma.$disconnect(); + } +} + +main().catch(() => process.exit(1)); From 1d9e37233ad3405a1f287a5a260a8ef345c5f02a Mon Sep 17 00:00:00 2001 From: Mathijs Rutgers Date: Wed, 12 Mar 2025 09:31:37 +0100 Subject: [PATCH 3/3] Use unless-stopped for restart strategy Co-authored-by: Reinand <133855289+Reinand@users.noreply.github.com> --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e12209c..5603cf6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,12 +11,12 @@ services: depends_on: db: condition: service_healthy - restart: always + restart: unless-stopped ports: - 3000:3000 db: image: mysql:latest - restart: always + restart: unless-stopped ports: - ${MYSQL_PORT}:${MYSQL_PORT} expose: