Merge pull request 'game workflow corrected' (#92) from Backend_Fix into main
Reviewed-on: #92
This commit was merged in pull request #92.
This commit is contained in:
@@ -68,8 +68,6 @@ export class StartGameCommandHandler {
|
|||||||
orgid: command.orgid || null,
|
orgid: command.orgid || null,
|
||||||
gamedecks,
|
gamedecks,
|
||||||
players: [],
|
players: [],
|
||||||
started: false,
|
|
||||||
finished: false,
|
|
||||||
winner: null,
|
winner: null,
|
||||||
state: GameState.WAITING,
|
state: GameState.WAITING,
|
||||||
startdate: null,
|
startdate: null,
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ export class StartGamePlayCommandHandler {
|
|||||||
|
|
||||||
// Update game state in database
|
// Update game state in database
|
||||||
const updatedGame = await this.gameRepository.update(game.id, {
|
const updatedGame = await this.gameRepository.update(game.id, {
|
||||||
started: true,
|
|
||||||
state: GameState.ACTIVE,
|
state: GameState.ACTIVE,
|
||||||
startdate: new Date()
|
startdate: new Date()
|
||||||
});
|
});
|
||||||
@@ -111,11 +110,6 @@ export class StartGamePlayCommandHandler {
|
|||||||
throw new Error('Game is not in waiting state and cannot be started');
|
throw new Error('Game is not in waiting state and cannot be started');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if game is already started
|
|
||||||
if (game.started) {
|
|
||||||
throw new Error('Game has already been started');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there are enough players (at least 2)
|
// Check if there are enough players (at least 2)
|
||||||
if (game.players.length < 2) {
|
if (game.players.length < 2) {
|
||||||
throw new Error('Game needs at least 2 players to start');
|
throw new Error('Game needs at least 2 players to start');
|
||||||
|
|||||||
@@ -1222,14 +1222,13 @@ export class GameWebSocketService {
|
|||||||
if (!game) return;
|
if (!game) return;
|
||||||
|
|
||||||
// Only clean up games that haven't finished yet
|
// Only clean up games that haven't finished yet
|
||||||
if (!game.finished) {
|
if (game.state !== GameState.FINISHED && game.state !== GameState.CANCELLED) {
|
||||||
logOther(`Handling abandoned game ${gameCode}`, { gameId: game.id });
|
logOther(`Handling abandoned game ${gameCode}`, { gameId: game.id });
|
||||||
|
|
||||||
// Mark game as abandoned in database
|
// Mark game as cancelled in database
|
||||||
await this.gameRepository.update(game.id, {
|
await this.gameRepository.update(game.id, {
|
||||||
finished: true,
|
state: GameState.CANCELLED,
|
||||||
enddate: new Date(),
|
enddate: new Date(),
|
||||||
// Could add an 'abandoned' flag if the database schema supports it
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clean up all Redis data for this abandoned game
|
// Clean up all Redis data for this abandoned game
|
||||||
@@ -2236,7 +2235,7 @@ export class GameWebSocketService {
|
|||||||
const game = await this.gameRepository.findByGameCode(gameCode);
|
const game = await this.gameRepository.findByGameCode(gameCode);
|
||||||
if (game) {
|
if (game) {
|
||||||
await this.gameRepository.update(game.id, {
|
await this.gameRepository.update(game.id, {
|
||||||
finished: true,
|
state: GameState.FINISHED,
|
||||||
winner: winnerId,
|
winner: winnerId,
|
||||||
enddate: new Date()
|
enddate: new Date()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||||
import { Consequence, CardType } from '../Deck/DeckAggregate';
|
import { Consequence, CardType } from '../Deck/DeckAggregate';
|
||||||
|
import { UserAggregate } from '../User/UserAggregate';
|
||||||
|
import { OrganizationAggregate } from '../Organization/OrganizationAggregate';
|
||||||
|
|
||||||
export enum GameState {
|
export enum GameState {
|
||||||
WAITING = 0,
|
WAITING = 0,
|
||||||
@@ -65,14 +67,8 @@ export class GameAggregate {
|
|||||||
@Column({ type: 'uuid', array: true, default: () => "'{}'", name: 'playerids' })
|
@Column({ type: 'uuid', array: true, default: () => "'{}'", name: 'playerids' })
|
||||||
players!: string[];
|
players!: string[];
|
||||||
|
|
||||||
@Column({ type: 'boolean', default: false })
|
@Column({ type: 'uuid', nullable: true, name: 'winnerId' })
|
||||||
started!: boolean;
|
winnerId!: string | null;
|
||||||
|
|
||||||
@Column({ type: 'boolean', default: false })
|
|
||||||
finished!: boolean;
|
|
||||||
|
|
||||||
@Column({ type: 'uuid', nullable: true, name: 'winnerid' })
|
|
||||||
winner!: string | null;
|
|
||||||
|
|
||||||
@Column({ type: 'int', default: GameState.WAITING })
|
@Column({ type: 'int', default: GameState.WAITING })
|
||||||
state!: GameState;
|
state!: GameState;
|
||||||
@@ -86,8 +82,20 @@ export class GameAggregate {
|
|||||||
@Column({ type: 'timestamp', nullable: true, name: 'finishDate' })
|
@Column({ type: 'timestamp', nullable: true, name: 'finishDate' })
|
||||||
enddate!: Date | null;
|
enddate!: Date | null;
|
||||||
|
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn({ name: 'updateDate' })
|
||||||
updateDate!: Date;
|
updateDate!: Date;
|
||||||
|
|
||||||
|
@ManyToOne(() => UserAggregate, { eager: false })
|
||||||
|
@JoinColumn({ name: 'createdBy' })
|
||||||
|
user!: UserAggregate | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => UserAggregate, { eager: false })
|
||||||
|
@JoinColumn({ name: 'winnerId' })
|
||||||
|
winner!: UserAggregate | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => OrganizationAggregate, { eager: false })
|
||||||
|
@JoinColumn({ name: 'organizationId' })
|
||||||
|
organization!: OrganizationAggregate | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Board Generation Types
|
// Board Generation Types
|
||||||
|
|||||||
@@ -1,32 +1,14 @@
|
|||||||
import { GameAggregate } from '../Game/GameAggregate';
|
import { GameAggregate, GameState } from '../Game/GameAggregate';
|
||||||
<<<<<<< HEAD
|
|
||||||
import { IPaginatedRepository } from './IBaseRepository';
|
import { IPaginatedRepository } from './IBaseRepository';
|
||||||
|
|
||||||
export interface IGameRepository extends IPaginatedRepository<GameAggregate, { games: GameAggregate[], totalCount: number }> {
|
export interface IGameRepository extends IPaginatedRepository<GameAggregate, { games: GameAggregate[], totalCount: number }> {
|
||||||
// Game-specific methods
|
// Game-specific methods
|
||||||
findByGameCode(gamecode: string): Promise<GameAggregate | null>;
|
findByGameCode(gamecode: string): Promise<GameAggregate | null>;
|
||||||
=======
|
|
||||||
|
|
||||||
export interface IGameRepository {
|
|
||||||
create(game: Partial<GameAggregate>): Promise<GameAggregate>;
|
|
||||||
findByPage(from: number, to: number): Promise<{ games: GameAggregate[], totalCount: number }>;
|
|
||||||
findByPageIncludingDeleted(from: number, to: number): Promise<{ games: GameAggregate[], totalCount: number }>;
|
|
||||||
findById(id: string): Promise<GameAggregate | null>;
|
|
||||||
findByIdIncludingDeleted(id: string): Promise<GameAggregate | null>;
|
|
||||||
findByGameCode(gamecode: string): Promise<GameAggregate | null>;
|
|
||||||
search(query: string, limit?: number, offset?: number): Promise<{ games: GameAggregate[], totalCount: number }>;
|
|
||||||
searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ games: GameAggregate[], totalCount: number }>;
|
|
||||||
update(id: string, update: Partial<GameAggregate>): Promise<GameAggregate | null>;
|
|
||||||
delete(id: string): Promise<any>;
|
|
||||||
softDelete(id: string): Promise<GameAggregate | null>;
|
|
||||||
|
|
||||||
// Game-specific methods
|
|
||||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
|
||||||
findActiveGames(): Promise<GameAggregate[]>;
|
findActiveGames(): Promise<GameAggregate[]>;
|
||||||
findGamesByPlayer(playerId: string): Promise<GameAggregate[]>;
|
findGamesByPlayer(playerId: string): Promise<GameAggregate[]>;
|
||||||
findWaitingGames(): Promise<GameAggregate[]>;
|
findWaitingGames(): Promise<GameAggregate[]>;
|
||||||
findFinishedGames(from?: number, to?: number): Promise<{ games: GameAggregate[], totalCount: number }>;
|
findFinishedGames(from?: number, to?: number): Promise<{ games: GameAggregate[], totalCount: number }>;
|
||||||
addPlayerToGame(gameId: string, playerId: string): Promise<GameAggregate | null>;
|
addPlayerToGame(gameId: string, playerId: string): Promise<GameAggregate | null>;
|
||||||
removePlayerFromGame(gameId: string, playerId: string): Promise<GameAggregate | null>;
|
removePlayerFromGame(gameId: string, playerId: string): Promise<GameAggregate | null>;
|
||||||
updateGameState(gameId: string, started: boolean, finished?: boolean, winner?: string): Promise<GameAggregate | null>;
|
updateGameState(gameId: string, state: GameState, winner?: string): Promise<GameAggregate | null>;
|
||||||
}
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
||||||
|
|
||||||
export class Full1757939815984 implements MigrationInterface {
|
|
||||||
name = 'Full1757939815984'
|
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`CREATE TABLE "Chats" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" character varying(50) NOT NULL DEFAULT 'direct', "name" character varying(255), "gameId" uuid, "createdBy" uuid, "users" uuid array NOT NULL, "messages" json NOT NULL DEFAULT '[]', "lastActivity" TIMESTAMP, "createDate" TIMESTAMP NOT NULL DEFAULT now(), "updateDate" TIMESTAMP NOT NULL DEFAULT now(), "state" integer NOT NULL DEFAULT '0', "archiveDate" TIMESTAMP, CONSTRAINT "PK_64c36c2b8d86a0d5de4cf64de8d" PRIMARY KEY ("id"))`);
|
|
||||||
await queryRunner.query(`CREATE TABLE "Users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "orgid" uuid, "username" character varying(100) NOT NULL, "password" character varying(255) NOT NULL, "email" character varying(255) NOT NULL, "fname" character varying(100) NOT NULL, "lname" character varying(100) NOT NULL, "token" character varying(255), "TokenExpires" TIMESTAMP, "phone" character varying(20), "state" integer NOT NULL DEFAULT '0', "regdate" TIMESTAMP NOT NULL DEFAULT now(), "updatedate" TIMESTAMP NOT NULL DEFAULT now(), "Orglogindate" TIMESTAMP, CONSTRAINT "UQ_ffc81a3b97dcbf8e320d5106c0d" UNIQUE ("username"), CONSTRAINT "UQ_3c3ab3f49a87e6ddb607f3c4945" UNIQUE ("email"), CONSTRAINT "PK_16d4f7d636df336db11d87413e3" PRIMARY KEY ("id"))`);
|
|
||||||
await queryRunner.query(`CREATE TABLE "Contacts" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "email" character varying(255) NOT NULL, "userid" uuid, "type" integer NOT NULL, "txt" text NOT NULL, "state" integer NOT NULL DEFAULT '0', "createDate" TIMESTAMP NOT NULL DEFAULT now(), "updateDate" TIMESTAMP NOT NULL DEFAULT now(), "adminResponse" text, "responseDate" TIMESTAMP, "respondedBy" uuid, CONSTRAINT "PK_68782cec65c8eef577c62958273" PRIMARY KEY ("id"))`);
|
|
||||||
await queryRunner.query(`CREATE TABLE "ChatArchives" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "chatId" uuid NOT NULL, "archivedMessages" json NOT NULL, "archivedAt" TIMESTAMP NOT NULL, "createDate" TIMESTAMP NOT NULL DEFAULT now(), "chatType" character varying(50) NOT NULL, "chatName" character varying(255), "gameId" uuid, "participants" uuid array NOT NULL, CONSTRAINT "PK_fe62979fc2061d7afe278d3f14e" PRIMARY KEY ("id"))`);
|
|
||||||
await queryRunner.query(`CREATE TABLE "Games" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "gamecode" character varying(10) NOT NULL, "maxplayers" integer NOT NULL, "logintype" integer NOT NULL DEFAULT '0', "createdby" character varying(255), "orgid" character varying(255), "gamedecks" json NOT NULL, "players" json NOT NULL DEFAULT '[]', "started" boolean NOT NULL DEFAULT false, "finished" boolean NOT NULL DEFAULT false, "winner" character varying(255), "state" integer NOT NULL DEFAULT '0', "create_date" TIMESTAMP NOT NULL DEFAULT now(), "start_date" TIMESTAMP, "end_date" TIMESTAMP, "update_date" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_9d52c646079cbe6f242a85c5c41" UNIQUE ("gamecode"), CONSTRAINT "PK_1950492f583d31609c5e9fbbe12" PRIMARY KEY ("id"))`);
|
|
||||||
await queryRunner.query(`CREATE TABLE "Organizations" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "contactfname" character varying(100) NOT NULL, "contactlname" character varying(100) NOT NULL, "contactphone" character varying(20) NOT NULL, "contactemail" character varying(255) NOT NULL, "state" integer NOT NULL DEFAULT '0', "regdate" TIMESTAMP NOT NULL DEFAULT now(), "updatedate" TIMESTAMP NOT NULL DEFAULT now(), "url" character varying(500), "userinorg" integer NOT NULL DEFAULT '0', "maxOrganizationalDecks" integer, CONSTRAINT "PK_e0690a31419f6666194423526f2" PRIMARY KEY ("id"))`);
|
|
||||||
await queryRunner.query(`CREATE TABLE "Decks" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "type" integer NOT NULL, "user_id" uuid NOT NULL, "creation_date" TIMESTAMP NOT NULL DEFAULT now(), "cards" json NOT NULL, "played_number" integer NOT NULL DEFAULT '0', "ctype" integer NOT NULL DEFAULT '0', "update_date" TIMESTAMP NOT NULL DEFAULT now(), "state" integer NOT NULL DEFAULT '0', "organization_id" uuid, CONSTRAINT "PK_001f26cb3ec39c1f25269943473" PRIMARY KEY ("id"))`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Decks" ADD CONSTRAINT "FK_06ee28f90d68543a03b14aebe13" FOREIGN KEY ("organization_id") REFERENCES "Organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`ALTER TABLE "Decks" DROP CONSTRAINT "FK_06ee28f90d68543a03b14aebe13"`);
|
|
||||||
await queryRunner.query(`DROP TABLE "Decks"`);
|
|
||||||
await queryRunner.query(`DROP TABLE "Organizations"`);
|
|
||||||
await queryRunner.query(`DROP TABLE "Games"`);
|
|
||||||
await queryRunner.query(`DROP TABLE "ChatArchives"`);
|
|
||||||
await queryRunner.query(`DROP TABLE "Contacts"`);
|
|
||||||
await queryRunner.query(`DROP TABLE "Users"`);
|
|
||||||
await queryRunner.query(`DROP TABLE "Chats"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
||||||
|
|
||||||
export class Full1758463929834 implements MigrationInterface {
|
|
||||||
name = 'Full1758463929834'
|
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "winner"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "create_date"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "end_date"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "update_date"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "boardsize" integer NOT NULL DEFAULT '50'`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "winnerid" uuid`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "createDate" TIMESTAMP NOT NULL DEFAULT now()`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "finishDate" TIMESTAMP`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "updateDate" TIMESTAMP NOT NULL DEFAULT now()`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "updateDate"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "finishDate"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "createDate"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "winnerid"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" DROP COLUMN "boardsize"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "update_date" TIMESTAMP NOT NULL DEFAULT now()`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "end_date" TIMESTAMP`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "create_date" TIMESTAMP NOT NULL DEFAULT now()`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "Games" ADD "winner" character varying(255)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class Full1762370334693 implements MigrationInterface {
|
||||||
|
name = 'Full1762370334693'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "Games" RENAME COLUMN "winnerid" TO "winnerId"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "Games" ADD CONSTRAINT "FK_330362bff8b25bb573f31fb4023" FOREIGN KEY ("winnerId") REFERENCES "Users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "Games" DROP CONSTRAINT "FK_330362bff8b25bb573f31fb4023"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "Games" RENAME COLUMN "winnerId" TO "winnerid"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
||||||
export class Full1758463928499 implements MigrationInterface {
|
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
}
|
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
export class Full1758463928499 implements MigrationInterface {
|
export class Full1762370333970 implements MigrationInterface {
|
||||||
|
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
}
|
}
|
||||||
@@ -385,19 +385,16 @@ export class GameRepository implements IGameRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateGameState(gameId: string, started: boolean, finished?: boolean, winner?: string): Promise<GameAggregate | null> {
|
async updateGameState(gameId: string, state: GameState, winner?: string): Promise<GameAggregate | null> {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
try {
|
try {
|
||||||
const updateData: Partial<GameAggregate> = { started };
|
const updateData: Partial<GameAggregate> = { state };
|
||||||
|
|
||||||
if (started && !finished) {
|
if (state === GameState.ACTIVE) {
|
||||||
updateData.state = GameState.ACTIVE;
|
|
||||||
updateData.startdate = new Date();
|
updateData.startdate = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finished) {
|
if (state === GameState.FINISHED) {
|
||||||
updateData.finished = true;
|
|
||||||
updateData.state = GameState.FINISHED;
|
|
||||||
updateData.enddate = new Date();
|
updateData.enddate = new Date();
|
||||||
if (winner) {
|
if (winner) {
|
||||||
updateData.winner = winner;
|
updateData.winner = winner;
|
||||||
@@ -407,7 +404,7 @@ export class GameRepository implements IGameRepository {
|
|||||||
const result = await this.update(gameId, updateData);
|
const result = await this.update(gameId, updateData);
|
||||||
|
|
||||||
const endTime = performance.now();
|
const endTime = performance.now();
|
||||||
logDatabase('Game state updated', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, started: ${started}, finished: ${finished}, winner: ${winner}`);
|
logDatabase('Game state updated', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, state: ${updateData.state}, winner: ${winner}`);
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const endTime = performance.now();
|
const endTime = performance.now();
|
||||||
|
|||||||
@@ -1,202 +1,172 @@
|
|||||||
-- SerpentRace Database Schema
|
-- This script was generated by the ERD tool in pgAdmin 4.
|
||||||
-- Generated from TypeORM Entity Aggregates
|
-- Please log an issue at https://github.com/pgadmin-org/pgadmin4/issues/new/choose if you find any bugs, including reproduction steps.
|
||||||
-- This file creates the complete database schema without initial data
|
BEGIN;
|
||||||
|
|
||||||
-- Enable UUID extension
|
|
||||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
||||||
|
|
||||||
-- Create Users table
|
CREATE TABLE IF NOT EXISTS public."ChatArchives"
|
||||||
CREATE TABLE "Users" (
|
(
|
||||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
"orgid" UUID NULL,
|
"chatId" uuid NOT NULL,
|
||||||
"username" VARCHAR(100) UNIQUE NOT NULL,
|
"archivedMessages" json NOT NULL,
|
||||||
"password" VARCHAR(255) NOT NULL,
|
"archivedAt" timestamp without time zone NOT NULL,
|
||||||
"email" VARCHAR(255) UNIQUE NOT NULL,
|
"createDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"fname" VARCHAR(100) NOT NULL,
|
"chatType" character varying(50) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"lname" VARCHAR(100) NOT NULL,
|
"chatName" character varying(255) COLLATE pg_catalog."default",
|
||||||
"token" VARCHAR(255) NULL,
|
"gameId" uuid,
|
||||||
"TokenExpires" TIMESTAMP NULL,
|
participants uuid[] NOT NULL,
|
||||||
"phone" VARCHAR(20) NULL,
|
CONSTRAINT "PK_fe62979fc2061d7afe278d3f14e" PRIMARY KEY (id)
|
||||||
"state" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"regdate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"Orglogindate" TIMESTAMP NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Create Organizations table
|
CREATE TABLE IF NOT EXISTS public."Chats"
|
||||||
CREATE TABLE "Organizations" (
|
(
|
||||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
"name" VARCHAR(255) NOT NULL,
|
type character varying(50) COLLATE pg_catalog."default" NOT NULL DEFAULT 'direct'::character varying,
|
||||||
"contactfname" VARCHAR(100) NOT NULL,
|
name character varying(255) COLLATE pg_catalog."default",
|
||||||
"contactlname" VARCHAR(100) NOT NULL,
|
"gameId" uuid,
|
||||||
"contactphone" VARCHAR(20) NOT NULL,
|
"createdBy" uuid,
|
||||||
"contactemail" VARCHAR(255) NOT NULL,
|
users uuid[] NOT NULL,
|
||||||
"state" INTEGER NOT NULL DEFAULT 0,
|
messages json NOT NULL DEFAULT '[]'::json,
|
||||||
"regdate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"lastActivity" timestamp without time zone,
|
||||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"url" VARCHAR(500) NULL,
|
"updateDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"userinorg" INTEGER NOT NULL DEFAULT 0,
|
state integer NOT NULL DEFAULT 0,
|
||||||
"maxOrganizationalDecks" INTEGER NULL
|
"archiveDate" timestamp without time zone,
|
||||||
|
CONSTRAINT "PK_64c36c2b8d86a0d5de4cf64de8d" PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Create Decks table
|
CREATE TABLE IF NOT EXISTS public."Contacts"
|
||||||
CREATE TABLE "Decks" (
|
(
|
||||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
"name" VARCHAR(255) NOT NULL,
|
name character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"type" INTEGER NOT NULL,
|
email character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"user_id" UUID NOT NULL,
|
userid uuid,
|
||||||
"creation_date" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
type integer NOT NULL,
|
||||||
"cards" JSONB NOT NULL DEFAULT '[]',
|
txt text COLLATE pg_catalog."default" NOT NULL,
|
||||||
"played_number" INTEGER NOT NULL DEFAULT 0,
|
state integer NOT NULL DEFAULT 0,
|
||||||
"ctype" INTEGER NOT NULL DEFAULT 0,
|
"createDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"updateDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"state" INTEGER NOT NULL DEFAULT 0,
|
"adminResponse" text COLLATE pg_catalog."default",
|
||||||
"organization_id" UUID NULL
|
"responseDate" timestamp without time zone,
|
||||||
|
"respondedBy" uuid,
|
||||||
|
CONSTRAINT "PK_68782cec65c8eef577c62958273" PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Create Chats table
|
CREATE TABLE IF NOT EXISTS public."Decks"
|
||||||
CREATE TABLE "Chats" (
|
(
|
||||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
"type" VARCHAR(50) NOT NULL DEFAULT 'direct',
|
name character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"name" VARCHAR(255) NULL,
|
type integer NOT NULL,
|
||||||
"gameId" UUID NULL,
|
user_id uuid NOT NULL,
|
||||||
"createdBy" UUID NULL,
|
creation_date timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"users" UUID[] NOT NULL,
|
cards json NOT NULL,
|
||||||
"messages" JSONB NOT NULL DEFAULT '[]',
|
played_number integer NOT NULL DEFAULT 0,
|
||||||
"lastActivity" TIMESTAMP NULL,
|
ctype integer NOT NULL DEFAULT 0,
|
||||||
"createDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"updateDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
state integer NOT NULL DEFAULT 0,
|
||||||
"state" INTEGER NOT NULL DEFAULT 0,
|
organization_id uuid,
|
||||||
"archiveDate" TIMESTAMP NULL
|
CONSTRAINT "PK_001f26cb3ec39c1f25269943473" PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Create Contacts table
|
CREATE TABLE IF NOT EXISTS public."Games"
|
||||||
CREATE TABLE "Contacts" (
|
(
|
||||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
"name" VARCHAR(255) NOT NULL,
|
gamecode character varying(10) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"email" VARCHAR(255) NOT NULL,
|
maxplayers integer NOT NULL,
|
||||||
"userid" UUID NULL,
|
logintype integer NOT NULL DEFAULT 0,
|
||||||
"type" INTEGER NOT NULL,
|
boardsize integer NOT NULL DEFAULT 50,
|
||||||
"txt" TEXT NOT NULL,
|
"createdBy" uuid NOT NULL,
|
||||||
"state" INTEGER NOT NULL DEFAULT 0,
|
organizationid uuid,
|
||||||
"createDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
decks jsonb NOT NULL DEFAULT '[]'::jsonb,
|
||||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
playerids uuid[] NOT NULL DEFAULT '{}'::uuid[],
|
||||||
"adminResponse" TEXT NULL,
|
"winnerId" uuid,
|
||||||
"responseDate" TIMESTAMP NULL,
|
state integer NOT NULL DEFAULT 0,
|
||||||
"respondedBy" UUID NULL
|
"createDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
|
start_date timestamp without time zone,
|
||||||
|
"finishDate" timestamp without time zone,
|
||||||
|
"updateDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
|
"organizationId" uuid,
|
||||||
|
CONSTRAINT "PK_1950492f583d31609c5e9fbbe12" PRIMARY KEY (id),
|
||||||
|
CONSTRAINT "UQ_9d52c646079cbe6f242a85c5c41" UNIQUE (gamecode)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Create Games table
|
CREATE TABLE IF NOT EXISTS public."Organizations"
|
||||||
CREATE TABLE "Games" (
|
(
|
||||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
"gamecode" VARCHAR(10) UNIQUE NOT NULL,
|
name character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"maxplayers" INTEGER NOT NULL,
|
contactfname character varying(100) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"logintype" INTEGER NOT NULL DEFAULT 0,
|
contactlname character varying(100) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"state" INTEGER NOT NULL DEFAULT 0,
|
contactphone character varying(20) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"playerids" UUID[] NOT NULL DEFAULT '{}',
|
contactemail character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
"decks" JSONB NOT NULL DEFAULT '[]',
|
state integer NOT NULL DEFAULT 0,
|
||||||
"boardsize" INTEGER NOT NULL DEFAULT 50,
|
regdate timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"createDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"updateDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
url character varying(500) COLLATE pg_catalog."default",
|
||||||
"finishDate" TIMESTAMP NULL,
|
userinorg integer NOT NULL DEFAULT 0,
|
||||||
"winnerid" UUID NULL,
|
"maxOrganizationalDecks" integer,
|
||||||
"createdBy" UUID NOT NULL,
|
CONSTRAINT "PK_e0690a31419f6666194423526f2" PRIMARY KEY (id)
|
||||||
"organizationid" UUID NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Add Foreign Key Constraints
|
CREATE TABLE IF NOT EXISTS public."Users"
|
||||||
ALTER TABLE "Users"
|
(
|
||||||
ADD CONSTRAINT "FK_Users_Organizations"
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
FOREIGN KEY ("orgid") REFERENCES "Organizations"("id") ON DELETE SET NULL;
|
orgid uuid,
|
||||||
|
username character varying(100) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
password character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
email character varying(255) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
fname character varying(100) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
lname character varying(100) COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
token character varying(255) COLLATE pg_catalog."default",
|
||||||
|
"TokenExpires" timestamp without time zone,
|
||||||
|
phone character varying(20) COLLATE pg_catalog."default",
|
||||||
|
state integer NOT NULL DEFAULT 0,
|
||||||
|
regdate timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
|
"updateDate" timestamp without time zone NOT NULL DEFAULT now(),
|
||||||
|
"Orglogindate" timestamp without time zone,
|
||||||
|
CONSTRAINT "PK_16d4f7d636df336db11d87413e3" PRIMARY KEY (id),
|
||||||
|
CONSTRAINT "UQ_3c3ab3f49a87e6ddb607f3c4945" UNIQUE (email),
|
||||||
|
CONSTRAINT "UQ_ffc81a3b97dcbf8e320d5106c0d" UNIQUE (username)
|
||||||
|
);
|
||||||
|
|
||||||
ALTER TABLE "Decks"
|
CREATE TABLE IF NOT EXISTS public.migrations
|
||||||
ADD CONSTRAINT "FK_Decks_Users"
|
(
|
||||||
FOREIGN KEY ("user_id") REFERENCES "Users"("id") ON DELETE CASCADE;
|
id serial NOT NULL,
|
||||||
|
"timestamp" bigint NOT NULL,
|
||||||
|
name character varying COLLATE pg_catalog."default" NOT NULL,
|
||||||
|
CONSTRAINT "PK_8c82d7f526340ab734260ea46be" PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
ALTER TABLE "Decks"
|
ALTER TABLE IF EXISTS public."Decks"
|
||||||
ADD CONSTRAINT "FK_Decks_Organizations"
|
ADD CONSTRAINT "FK_06ee28f90d68543a03b14aebe13" FOREIGN KEY (organization_id)
|
||||||
FOREIGN KEY ("organization_id") REFERENCES "Organizations"("id") ON DELETE SET NULL;
|
REFERENCES public."Organizations" (id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE NO ACTION;
|
||||||
|
|
||||||
ALTER TABLE "Contacts"
|
|
||||||
ADD CONSTRAINT "FK_Contacts_Users"
|
|
||||||
FOREIGN KEY ("userid") REFERENCES "Users"("id") ON DELETE SET NULL;
|
|
||||||
|
|
||||||
ALTER TABLE "Contacts"
|
ALTER TABLE IF EXISTS public."Decks"
|
||||||
ADD CONSTRAINT "FK_Contacts_RespondedBy"
|
ADD CONSTRAINT "FK_a39059433e29882e1309d3a5e70" FOREIGN KEY (user_id)
|
||||||
FOREIGN KEY ("respondedBy") REFERENCES "Users"("id") ON DELETE SET NULL;
|
REFERENCES public."Users" (id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE NO ACTION;
|
||||||
|
|
||||||
ALTER TABLE "Chats"
|
|
||||||
ADD CONSTRAINT "FK_Chats_CreatedBy"
|
|
||||||
FOREIGN KEY ("createdBy") REFERENCES "Users"("id") ON DELETE SET NULL;
|
|
||||||
|
|
||||||
ALTER TABLE "Chats"
|
ALTER TABLE IF EXISTS public."Games"
|
||||||
ADD CONSTRAINT "FK_Chats_Games"
|
ADD CONSTRAINT "FK_330362bff8b25bb573f31fb4023" FOREIGN KEY ("winnerId")
|
||||||
FOREIGN KEY ("gameId") REFERENCES "Games"("id") ON DELETE SET NULL;
|
REFERENCES public."Users" (id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE NO ACTION;
|
||||||
|
|
||||||
ALTER TABLE "Games"
|
|
||||||
ADD CONSTRAINT "FK_Games_CreatedBy"
|
|
||||||
FOREIGN KEY ("createdBy") REFERENCES "Users"("id") ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE "Games"
|
ALTER TABLE IF EXISTS public."Games"
|
||||||
ADD CONSTRAINT "FK_Games_Organizations"
|
ADD CONSTRAINT "FK_e3c4e8898fa026a5551aefc4f62" FOREIGN KEY ("organizationId")
|
||||||
FOREIGN KEY ("organizationid") REFERENCES "Organizations"("id") ON DELETE SET NULL;
|
REFERENCES public."Organizations" (id) MATCH SIMPLE
|
||||||
|
ON UPDATE NO ACTION
|
||||||
|
ON DELETE NO ACTION;
|
||||||
|
|
||||||
ALTER TABLE "Games"
|
|
||||||
ADD CONSTRAINT "FK_Games_Winner"
|
|
||||||
FOREIGN KEY ("winnerid") REFERENCES "Users"("id") ON DELETE SET NULL;
|
|
||||||
|
|
||||||
-- Create Indexes for Performance
|
ALTER TABLE IF EXISTS public."Games"
|
||||||
CREATE INDEX "IDX_Users_Username" ON "Users" ("username");
|
ADD CONSTRAINT "FK_f32db60863a8a393b30aa222cd5" FOREIGN KEY ("createdBy")
|
||||||
CREATE INDEX "IDX_Users_Email" ON "Users" ("email");
|
REFERENCES public."Users" (id) MATCH SIMPLE
|
||||||
CREATE INDEX "IDX_Users_OrgId" ON "Users" ("orgid");
|
ON UPDATE NO ACTION
|
||||||
CREATE INDEX "IDX_Users_State" ON "Users" ("state");
|
ON DELETE NO ACTION;
|
||||||
|
|
||||||
CREATE INDEX "IDX_Organizations_Name" ON "Organizations" ("name");
|
END;
|
||||||
CREATE INDEX "IDX_Organizations_State" ON "Organizations" ("state");
|
|
||||||
|
|
||||||
CREATE INDEX "IDX_Decks_UserId" ON "Decks" ("user_id");
|
|
||||||
CREATE INDEX "IDX_Decks_Type" ON "Decks" ("type");
|
|
||||||
CREATE INDEX "IDX_Decks_CType" ON "Decks" ("ctype");
|
|
||||||
CREATE INDEX "IDX_Decks_State" ON "Decks" ("state");
|
|
||||||
CREATE INDEX "IDX_Decks_OrganizationId" ON "Decks" ("organization_id");
|
|
||||||
|
|
||||||
CREATE INDEX "IDX_Chats_Type" ON "Chats" ("type");
|
|
||||||
CREATE INDEX "IDX_Chats_State" ON "Chats" ("state");
|
|
||||||
CREATE INDEX "IDX_Chats_GameId" ON "Chats" ("gameId");
|
|
||||||
CREATE INDEX "IDX_Chats_CreatedBy" ON "Chats" ("createdBy");
|
|
||||||
|
|
||||||
CREATE INDEX "IDX_Contacts_Type" ON "Contacts" ("type");
|
|
||||||
CREATE INDEX "IDX_Contacts_State" ON "Contacts" ("state");
|
|
||||||
CREATE INDEX "IDX_Contacts_UserId" ON "Contacts" ("userid");
|
|
||||||
|
|
||||||
CREATE INDEX "IDX_Games_GameCode" ON "Games" ("gamecode");
|
|
||||||
CREATE INDEX "IDX_Games_State" ON "Games" ("state");
|
|
||||||
CREATE INDEX "IDX_Games_CreatedBy" ON "Games" ("createdBy");
|
|
||||||
CREATE INDEX "IDX_Games_OrganizationId" ON "Games" ("organizationid");
|
|
||||||
|
|
||||||
-- Comments for documentation
|
|
||||||
COMMENT ON TABLE "Users" IS 'User accounts with authentication and profile information';
|
|
||||||
COMMENT ON TABLE "Organizations" IS 'Organizations that can have multiple users and premium features';
|
|
||||||
COMMENT ON TABLE "Decks" IS 'Card decks for the game, can be public, private, or organizational';
|
|
||||||
COMMENT ON TABLE "Chats" IS 'Chat system supporting direct messages, groups, and game chats';
|
|
||||||
COMMENT ON TABLE "Contacts" IS 'Contact form submissions and support tickets';
|
|
||||||
COMMENT ON TABLE "Games" IS 'Game sessions with players, decks, and game state';
|
|
||||||
|
|
||||||
-- Enum value comments
|
|
||||||
COMMENT ON COLUMN "Users"."state" IS '0=REGISTERED_NOT_VERIFIED, 1=VERIFIED_REGULAR, 2=VERIFIED_PREMIUM, 3=SOFT_DELETE, 4=DEACTIVATED, 5=ADMIN';
|
|
||||||
COMMENT ON COLUMN "Organizations"."state" IS '0=REGISTERED, 1=ACTIVE, 2=SOFT_DELETE';
|
|
||||||
COMMENT ON COLUMN "Decks"."type" IS '0=LUCK, 1=JOKER, 2=QUESTION';
|
|
||||||
COMMENT ON COLUMN "Decks"."ctype" IS '0=PUBLIC, 1=PRIVATE, 2=ORGANIZATION';
|
|
||||||
COMMENT ON COLUMN "Decks"."state" IS '0=ACTIVE, 1=SOFT_DELETE';
|
|
||||||
COMMENT ON COLUMN "Chats"."type" IS 'direct, group, game';
|
|
||||||
COMMENT ON COLUMN "Chats"."state" IS '0=ACTIVE, 1=ARCHIVE, 2=SOFT_DELETE';
|
|
||||||
COMMENT ON COLUMN "Contacts"."type" IS '0=BUG, 1=PROBLEM, 2=QUESTION, 3=SALES, 4=OTHER';
|
|
||||||
COMMENT ON COLUMN "Contacts"."state" IS '0=ACTIVE, 1=RESOLVED, 2=SOFT_DELETE';
|
|
||||||
COMMENT ON COLUMN "Games"."state" IS '0=WAITING, 1=ACTIVE, 2=FINISHED, 3=CANCELLED';
|
|
||||||
COMMENT ON COLUMN "Games"."logintype" IS '0=PUBLIC, 1=PRIVATE, 2=ORGANIZATION';
|
|
||||||
|
|
||||||
-- Grant permissions for application user
|
|
||||||
-- Note: Replace 'serpentrace_app' with your actual application database user
|
|
||||||
-- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO serpentrace_app;
|
|
||||||
-- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO serpentrace_app;
|
|
||||||
-- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO serpentrace_app;
|
|
||||||
Reference in New Issue
Block a user