https://project.mdnd-it.cc/work_packages/94
This commit is contained in:
2025-08-23 04:25:28 +02:00
parent 725516ad6c
commit 19cfa031d0
25823 changed files with 1095586 additions and 2801759 deletions
@@ -0,0 +1,64 @@
import { Entity, PrimaryGeneratedColumn, Column, UpdateDateColumn, CreateDateColumn } from 'typeorm';
export interface Message {
id: string; // UUID for each message
date: Date;
userid: string; // UUID reference to UserAggregate
text: string;
}
export const ChatState = {
ACTIVE: 0,
ARCHIVE: 1,
SOFT_DELETE: 2
} as const;
export type ChatStateType = typeof ChatState[keyof typeof ChatState];
export const ChatType = {
DIRECT: 'direct',
GROUP: 'group',
GAME: 'game'
} as const;
export type ChatTypeType = typeof ChatType[keyof typeof ChatType];
@Entity('Chats')
export class ChatAggregate {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'varchar', length: 50, default: ChatType.DIRECT })
type!: ChatTypeType;
@Column({ type: 'varchar', length: 255, nullable: true })
name!: string | null; // Group name or Game name
@Column({ type: 'uuid', nullable: true })
gameId!: string | null; // Game UUID reference for game chats
@Column({ type: 'uuid', nullable: true })
createdBy!: string | null; // User who created the group/chat
@Column('uuid', { array: true })
users!: string[]; // Active participants
@Column('json', { default: [] })
messages!: Message[]; // Active messages (last 10 per user, max 2 weeks)
@Column({ type: 'timestamp', nullable: true })
lastActivity!: Date | null;
@CreateDateColumn()
createDate!: Date;
@UpdateDateColumn()
updateDate!: Date;
@Column({ type: 'int', default: ChatState.ACTIVE })
state!: ChatStateType;
// Archive when inactive for specified period
@Column({ type: 'timestamp', nullable: true })
archiveDate!: Date | null;
}
@@ -0,0 +1,33 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
import { Message } from './ChatAggregate';
@Entity('ChatArchives')
export class ChatArchiveAggregate {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'uuid' })
chatId!: string; // Reference to original chat
@Column('json')
archivedMessages!: Message[]; // All archived messages
@Column({ type: 'timestamp' })
archivedAt!: Date;
@CreateDateColumn()
createDate!: Date;
// Metadata for context
@Column({ type: 'varchar', length: 50 })
chatType!: string; // direct, group, game
@Column({ type: 'varchar', length: 255, nullable: true })
chatName!: string | null;
@Column({ type: 'uuid', nullable: true })
gameId!: string | null;
@Column('uuid', { array: true })
participants!: string[]; // Users who participated
}
@@ -0,0 +1,55 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
export enum ContactType {
BUG = 0,
PROBLEM = 1,
QUESTION = 2,
SALES = 3,
OTHER = 4
}
export enum ContactState {
ACTIVE = 0,
RESOLVED = 1,
SOFT_DELETE = 2
}
@Entity('Contacts')
export class ContactAggregate {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'varchar', length: 255 })
name!: string;
@Column({ type: 'varchar', length: 255 })
email!: string;
@Column({ type: 'uuid', nullable: true })
userid!: string | null; // If logged in user
@Column({ type: 'int' })
type!: ContactType;
@Column({ type: 'text' })
txt!: string;
@Column({ type: 'int', default: ContactState.ACTIVE })
state!: ContactState;
@CreateDateColumn()
createDate!: Date;
@UpdateDateColumn()
updateDate!: Date;
// Admin response field for email response feature
@Column({ type: 'text', nullable: true })
adminResponse!: string | null;
@Column({ type: 'timestamp', nullable: true })
responseDate!: Date | null;
@Column({ type: 'uuid', nullable: true })
respondedBy!: string | null; // Admin user id who responded
}
@@ -0,0 +1,70 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { OrganizationAggregate } from '../Organization/OrganizationAggregate';
export enum Type {
LUCK = 0,
JOKER = 1,
QUESTION = 2
}
export enum CType {
PUBLIC = 0,
PRIVATE = 1,
ORGANIZATION = 2
}
export enum State {
ACTIVE = 0,
SOFT_DELETE = 1
}
export enum CardType {
QUIZ = 0,
SENTENCE_PAIRING = 1,
OWN_ANSWER = 2,
TRUE_FALSE = 3,
CLOSER = 4
}
export interface Card {
text: string;
type?: CardType;
answer?: string | null;
}
@Entity('Decks')
export class DeckAggregate {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'varchar', length: 255 })
name!: string;
@Column({ type: 'int'})
type!: Type;
@Column({ type: 'uuid', name: 'user_id' })
userid!: string;
@CreateDateColumn({ name: 'creation_date' })
creationdate!: Date;
@Column({ type: 'json' })
cards!: Card[];
@Column({ type: 'int', default: 0, name: 'played_number' })
playedNumber!: number;
@Column({ type: 'int', default: CType.PUBLIC })
ctype!: CType;
@UpdateDateColumn({ name: 'update_date' })
updatedate!: Date;
@Column({ type: 'int', default: State.ACTIVE })
state!: State;
@ManyToOne(() => OrganizationAggregate, { nullable: true })
@JoinColumn({ name: 'organization_id' })
organization!: OrganizationAggregate | null;
}
@@ -0,0 +1,11 @@
import { ChatArchiveAggregate } from '../Chat/ChatArchiveAggregate';
export interface IChatArchiveRepository {
create(archive: Partial<ChatArchiveAggregate>): Promise<ChatArchiveAggregate>;
findAll(): Promise<ChatArchiveAggregate[]>;
findById(id: string): Promise<ChatArchiveAggregate | null>;
findByChatId(chatId: string): Promise<ChatArchiveAggregate[]>;
findByGameId(gameId: string): Promise<ChatArchiveAggregate[]>;
delete(id: string): Promise<any>;
cleanup(olderThanDays: number): Promise<number>; // Clean up old archives
}
@@ -0,0 +1,21 @@
import { ChatAggregate } from '../Chat/ChatAggregate';
import { ChatArchiveAggregate } from '../Chat/ChatArchiveAggregate';
export interface IChatRepository {
create(chat: Partial<ChatAggregate>): Promise<ChatAggregate>;
findByPage(from: number, to: number): Promise<{ chats: ChatAggregate[], totalCount: number }>;
findByPageIncludingDeleted(from: number, to: number): Promise<{ chats: ChatAggregate[], totalCount: number }>;
findById(id: string): Promise<ChatAggregate | null>;
findByIdIncludingDeleted(id: string): Promise<ChatAggregate | null>;
findByUserId(userId: string): Promise<ChatAggregate[]>;
findByUserIdIncludingDeleted(userId: string): Promise<ChatAggregate[]>;
findByGameId(gameId: string): Promise<ChatAggregate | null>;
findActiveChatsForUser(userId: string): Promise<ChatAggregate[]>;
findInactiveChats(inactivityMinutes: number): Promise<ChatAggregate[]>;
update(id: string, update: Partial<ChatAggregate>): Promise<ChatAggregate | null>;
delete(id: string): Promise<any>;
softDelete(id: string): Promise<ChatAggregate | null>;
archiveChat(chat: ChatAggregate): Promise<ChatArchiveAggregate>;
getArchivedChat(chatId: string): Promise<ChatArchiveAggregate | null>;
restoreFromArchive(chatId: string): Promise<ChatAggregate | null>;
}
@@ -0,0 +1,14 @@
import { ContactAggregate } from '../Contact/ContactAggregate';
export interface IContactRepository {
create(contact: Partial<ContactAggregate>): Promise<ContactAggregate>;
findById(id: string): Promise<ContactAggregate | null>;
findByPage(from: number, to: number): Promise<{ contacts: ContactAggregate[], totalCount: number }>;
findByPageIncludingDeleted(from: number, to: number): Promise<{ contacts: ContactAggregate[], totalCount: number }>;
update(id: string, update: Partial<ContactAggregate>): Promise<ContactAggregate | null>;
delete(id: string): Promise<any>;
softDelete(id: string): Promise<ContactAggregate | null>;
findByIdIncludingDeleted(id: string): Promise<ContactAggregate | null>;
search(searchTerm: string): Promise<ContactAggregate[]>;
searchIncludingDeleted(searchTerm: string): Promise<ContactAggregate[]>;
}
@@ -0,0 +1,19 @@
import { DeckAggregate } from '../Deck/DeckAggregate';
export interface IDeckRepository {
create(deck: Partial<DeckAggregate>): Promise<DeckAggregate>;
findByPage(from: number, to: number): Promise<{ decks: DeckAggregate[], totalCount: number }>;
findByPageIncludingDeleted(from: number, to: number): Promise<{ decks: DeckAggregate[], totalCount: number }>;
findById(id: string): Promise<DeckAggregate | null>;
findByIdIncludingDeleted(id: string): Promise<DeckAggregate | null>;
search(query: string, limit?: number, offset?: number): Promise<{ decks: DeckAggregate[], totalCount: number }>;
searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ decks: DeckAggregate[], totalCount: number }>;
update(id: string, update: Partial<DeckAggregate>): Promise<DeckAggregate | null>;
delete(id: string): Promise<any>;
softDelete(id: string): Promise<DeckAggregate | null>;
// New methods for deck restrictions and filtering
countActiveByUserId(userId: string): Promise<number>;
countOrganizationalByUserId(userId: string): Promise<number>;
findFilteredDecks(userId: string, userOrgId?: string | null, isAdmin?: boolean, from?: number, to?: number): Promise<{ decks: DeckAggregate[], totalCount: number }>;
}
@@ -0,0 +1,14 @@
import { OrganizationAggregate } from '../Organization/OrganizationAggregate';
export interface IOrganizationRepository {
create(org: Partial<OrganizationAggregate>): Promise<OrganizationAggregate>;
findByPage(from: number, to: number): Promise<{ organizations: OrganizationAggregate[], totalCount: number }>;
findByPageIncludingDeleted(from: number, to: number): Promise<{ organizations: OrganizationAggregate[], totalCount: number }>;
findById(id: string): Promise<OrganizationAggregate | null>;
findByIdIncludingDeleted(id: string): Promise<OrganizationAggregate | null>;
search(query: string, limit?: number, offset?: number): Promise<{ organizations: OrganizationAggregate[], totalCount: number }>;
searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ organizations: OrganizationAggregate[], totalCount: number }>;
update(id: string, update: Partial<OrganizationAggregate>): Promise<OrganizationAggregate | null>;
delete(id: string): Promise<any>;
softDelete(id: string): Promise<OrganizationAggregate | null>;
}
@@ -0,0 +1,18 @@
import { UserAggregate } from '../User/UserAggregate';
export interface IUserRepository {
create(user: Partial<UserAggregate>): Promise<UserAggregate>;
findByPage(from: number, to: number): Promise<{ users: UserAggregate[], totalCount: number }>;
findByPageIncludingDeleted(from: number, to: number): Promise<{ users: UserAggregate[], totalCount: number }>;
findById(id: string): Promise<UserAggregate | null>;
findByIdIncludingDeleted(id: string): Promise<UserAggregate | null>;
findByUsername(username: string): Promise<UserAggregate | null>;
findByEmail(email: string): Promise<UserAggregate | null>;
findByToken(token: string): Promise<UserAggregate | null>;
search(query: string, limit?: number, offset?: number): Promise<{ users: UserAggregate[], totalCount: number }>;
searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ users: UserAggregate[], totalCount: number }>;
update(id: string, update: Partial<UserAggregate>): Promise<UserAggregate | null>;
delete(id: string): Promise<any>;
softDelete(id: string): Promise<UserAggregate | null>;
deactivate(id: string): Promise<UserAggregate | null>;
}
@@ -0,0 +1,52 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
import { UserAggregate } from '../User/UserAggregate';
export const OrganizationState = {
REGISTERED: 0,
ACTIVE: 1,
SOFT_DELETE: 2
} as const;
export type OrganizationStateType = typeof OrganizationState[keyof typeof OrganizationState];
@Entity('Organizations')
export class OrganizationAggregate {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'varchar', length: 255 })
name!: string;
@Column({ type: 'varchar', length: 100 })
contactfname!: string;
@Column({ type: 'varchar', length: 100 })
contactlname!: string;
@Column({ type: 'varchar', length: 20 })
contactphone!: string;
@Column({ type: 'varchar', length: 255 })
contactemail!: string;
@Column({ type: 'int', default: OrganizationState.REGISTERED })
state!: OrganizationStateType;
@CreateDateColumn()
regdate!: Date;
@UpdateDateColumn()
updatedate!: Date;
@Column({ type: 'varchar', length: 500, nullable: true })
url!: string | null;
@Column({ type: 'int', default: 0 })
userinorg!: number;
@Column({ type: 'int', nullable: true })
maxOrganizationalDecks!: number | null;
@OneToMany(() => UserAggregate, user => user.orgid)
users!: UserAggregate[];
}
@@ -0,0 +1,61 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
export enum UserState {
REGISTERED_NOT_VERIFIED = 0,
VERIFIED_REGULAR = 1,
VERIFIED_PREMIUM = 2,
SOFT_DELETE = 3,
DEACTIVATED = 4,
ADMIN = 5
}
@Entity('Users')
export class UserAggregate {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'uuid', nullable: true })
orgid!: string | null;
@Column({ type: 'varchar', length: 100, unique: true })
username!: string;
@Column({ type: 'varchar', length: 255 })
password!: string;
@Column({ type: 'varchar', length: 255, unique: true })
email!: string;
@Column({ type: 'varchar', length: 100 })
fname!: string;
@Column({ type: 'varchar', length: 100 })
lname!: string;
@Column({ type: 'varchar', length: 255, nullable: true })
token!: string | null;
@Column({ type: 'timestamp', nullable: true })
TokenExpires!: Date | null;
@Column({ type: 'varchar', length: 50 })
type!: string;
@Column({ type: 'varchar', length: 20, nullable: true })
phone!: string | null;
@Column({
type: 'int',
default: UserState.REGISTERED_NOT_VERIFIED
})
state!: UserState;
@CreateDateColumn()
regdate!: Date;
@UpdateDateColumn()
updatedate!: Date;
@Column({ type: 'timestamp', nullable: true })
Orglogindate!: Date | null;
}