Compare commits

..

2 Commits

Author SHA1 Message Date
db747c4f7a hopefully docker fix 2026-03-29 17:45:17 +08:00
c88bb5d2c4 production hardening 2026-03-29 01:51:42 +08:00
11 changed files with 63 additions and 45 deletions

12
.dockerignore Normal file
View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
generator client {
provider = "prisma-client-js"
output = "../node_modules/.prisma/client"
}
datasource db {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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