Compare commits

..

13 Commits

11 changed files with 45 additions and 63 deletions

View File

@@ -1,12 +0,0 @@
.git
.github
.node_modules
dist
coverage
*.log
.env*
test
*.spec.ts
*.e2e-spec.ts
README.md
AGENTS.md

View File

@@ -8,11 +8,9 @@ RUN pnpm build
FROM node:20-alpine FROM node:20-alpine
WORKDIR /app WORKDIR /app
RUN npm i -g pnpm COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
COPY --from=builder /app/dist ./dist COPY --from=builder /app/package.json ./package.json
RUN pnpm install --prod --frozen-lockfile
CMD ["node", "dist/src/main.js"] CMD ["node", "dist/src/main.js"]

View File

@@ -47,9 +47,9 @@
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"ioredis": "^5.8.2", "ioredis": "^5.8.2",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"helmet": "^8.1.0",
"passport": "^0.7.0", "passport": "^0.7.0",
"passport-discord": "^0.1.4", "passport-discord": "^0.1.4",
"helmet": "^8.1.0",
"passport-google-oauth20": "^2.0.0", "passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"pg": "^8.16.3", "pg": "^8.16.3",

View File

@@ -3,7 +3,6 @@
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
output = "../node_modules/.prisma/client"
} }
datasource db { datasource db {
@@ -50,10 +49,10 @@ model User {
activeDollId String? @map("active_doll_id") activeDollId String? @map("active_doll_id")
activeDoll Doll? @relation("ActiveDoll", fields: [activeDollId], references: [id]) activeDoll Doll? @relation("ActiveDoll", fields: [activeDollId], references: [id])
sentFriendRequests FriendRequest[] @relation("SentFriendRequests") sentFriendRequests FriendRequest[] @relation("SentFriendRequests")
receivedFriendRequests FriendRequest[] @relation("ReceivedFriendRequests") receivedFriendRequests FriendRequest[] @relation("ReceivedFriendRequests")
userFriendships Friendship[] @relation("UserFriendships") userFriendships Friendship[] @relation("UserFriendships")
friendFriendships Friendship[] @relation("FriendFriendships") friendFriendships Friendship[] @relation("FriendFriendships")
dolls Doll[] dolls Doll[]
authIdentities AuthIdentity[] authIdentities AuthIdentity[]
authSessions AuthSession[] authSessions AuthSession[]
@@ -63,17 +62,17 @@ model User {
} }
model AuthIdentity { model AuthIdentity {
id String @id @default(uuid()) id String @id @default(uuid())
provider AuthProvider provider AuthProvider
providerSubject String @map("provider_subject") providerSubject String @map("provider_subject")
providerEmail String? @map("provider_email") providerEmail String? @map("provider_email")
providerName String? @map("provider_name") providerName String? @map("provider_name")
providerUsername String? @map("provider_username") providerUsername String? @map("provider_username")
providerPicture String? @map("provider_picture") providerPicture String? @map("provider_picture")
emailVerified Boolean @default(false) @map("email_verified") emailVerified Boolean @default(false) @map("email_verified")
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
userId String @map("user_id") userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -83,14 +82,14 @@ model AuthIdentity {
} }
model AuthSession { model AuthSession {
id String @id @default(uuid()) id String @id @default(uuid())
provider AuthProvider? provider AuthProvider?
refreshTokenHash String @unique @map("refresh_token_hash") refreshTokenHash String @unique @map("refresh_token_hash")
expiresAt DateTime @map("expires_at") expiresAt DateTime @map("expires_at")
revokedAt DateTime? @map("revoked_at") revokedAt DateTime? @map("revoked_at")
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
userId String @map("user_id") userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -99,13 +98,13 @@ model AuthSession {
} }
model AuthExchangeCode { model AuthExchangeCode {
id String @id @default(uuid()) id String @id @default(uuid())
provider AuthProvider provider AuthProvider
codeHash String @unique @map("code_hash") codeHash String @unique @map("code_hash")
expiresAt DateTime @map("expires_at") expiresAt DateTime @map("expires_at")
consumedAt DateTime? @map("consumed_at") consumedAt DateTime? @map("consumed_at")
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
userId String @map("user_id") userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)

View File

@@ -41,7 +41,7 @@ function validateEnvironment(
} }
// Validate PORT if provided // Validate PORT if provided
if (config.PORT && isNaN(Number(config.PORT))) { if (config.PORT !== undefined && !Number.isFinite(Number(config.PORT))) {
throw new Error('PORT must be a valid number'); throw new Error('PORT must be a valid number');
} }

View File

@@ -108,10 +108,6 @@ class RedisLifecycleService implements OnModuleDestroy {
}, },
}); });
client.on('error', (err) => {
logger.error('Redis connection error', err);
});
client.on('connect', () => { client.on('connect', () => {
logger.log(`Connected to Redis at ${host}:${port}`); logger.log(`Connected to Redis at ${host}:${port}`);
}); });

View File

@@ -9,9 +9,9 @@ export type AuthenticatedSocket = BaseSocket<
{ {
user?: AuthenticatedUser; user?: AuthenticatedUser;
userId?: string; userId?: string;
senderName?: string;
senderNameCachedAt?: number;
activeDollId?: string | null; activeDollId?: string | null;
friends?: Set<string>; // Set of friend user IDs friends?: Set<string>; // Set of friend user IDs
senderName?: string;
senderNameCachedAt?: number;
} }
>; >;

View File

@@ -41,7 +41,6 @@ export class ConnectionHandler {
// Initialize defaults // Initialize defaults
client.data.activeDollId = null; client.data.activeDollId = null;
client.data.friends = new Set(); client.data.friends = new Set();
client.data.senderName = undefined;
// userId is not set yet, it will be set in handleClientInitialize // userId is not set yet, it will be set in handleClientInitialize
this.logger.log(`WebSocket authenticated (Pending Init): ${payload.sub}`); this.logger.log(`WebSocket authenticated (Pending Init): ${payload.sub}`);

View File

@@ -1,9 +1,9 @@
import { Logger } from '@nestjs/common'; import { Logger } from '@nestjs/common';
import { WsException } from '@nestjs/websockets'; import { WsException } from '@nestjs/websockets';
import type { AuthenticatedSocket } from '../../../types/socket'; import type { AuthenticatedSocket } from '../../../types/socket';
import { PrismaService } from '../../../database/prisma.service';
import { SendInteractionDto } from '../../dto/send-interaction.dto'; import { SendInteractionDto } from '../../dto/send-interaction.dto';
import { InteractionPayloadDto } from '../../dto/interaction-payload.dto'; import { InteractionPayloadDto } from '../../dto/interaction-payload.dto';
import { PrismaService } from '../../../database/prisma.service';
import { UserSocketService } from '../user-socket.service'; import { UserSocketService } from '../user-socket.service';
import { WsNotificationService } from '../ws-notification.service'; import { WsNotificationService } from '../ws-notification.service';
import { WS_EVENT } from '../ws-events'; import { WS_EVENT } from '../ws-events';

View File

@@ -22,6 +22,8 @@ type MockSocket = {
userId?: string; userId?: string;
activeDollId?: string | null; activeDollId?: string | null;
friends?: Set<string>; friends?: Set<string>;
senderName?: string;
senderNameCachedAt?: number;
}; };
handshake?: any; handshake?: any;
disconnect?: jest.Mock; disconnect?: jest.Mock;

View File

@@ -132,12 +132,6 @@ export class StateGateway
} }
} }
onModuleDestroy() {
if (this.redisSubscriber) {
this.redisSubscriber.removeAllListeners('message');
}
}
async isUserOnline(userId: string): Promise<boolean> { async isUserOnline(userId: string): Promise<boolean> {
return this.userSocketService.isUserOnline(userId); return this.userSocketService.isUserOnline(userId);
} }
@@ -165,4 +159,10 @@ export class StateGateway
) { ) {
await this.interactionHandler.handleSendInteraction(client, data); await this.interactionHandler.handleSendInteraction(client, data);
} }
onModuleDestroy() {
if (this.redisSubscriber) {
this.redisSubscriber.removeAllListeners('message');
}
}
} }