Commit 1776bb7a authored by 何 广一's avatar 何 广一
Browse files

created docker image

parent 9bf2ef80
module.exports = {
ignorePatterns: ['src/generated/prisma/**'],
extends: ['next/core-web-vitals'],
rules: {},
};
\ No newline at end of file
......@@ -3,7 +3,7 @@
FROM node:20-alpine AS base
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile --shamefully-hoist
RUN npm install -g pnpm && pnpm install --shamefully-hoist
# ---------- Prisma + SQLite ----------
COPY prisma ./prisma
......@@ -14,15 +14,17 @@ RUN pnpm prisma migrate deploy --schema ./prisma/schema.prisma
# ---------- 构建 ----------
COPY . .
ENV REDIS_URL=
RUN pnpm build
# ---------- 运行时 ----------
FROM node:20-alpine AS runner
WORKDIR /app
RUN apk add --no-cache redis
COPY --from=base /app/.next ./.next
COPY --from=base /app/node_modules ./node_modules
COPY --from=base /app/package.json ./package.json
COPY --from=base /app/.next/standalone ./
COPY --from=base /app/.next/static ./.next/static
COPY --from=base /app/public ./public
COPY --from=base /app/prisma ./prisma
COPY --from=base /app/.env.docker ./.env
......
......@@ -11,6 +11,10 @@ const compat = new FlatCompat({
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{
ignores: ["src/generated/prisma/**"],
},
];
export default eslintConfig;
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: 'standalone'
/* config options here */
};
......
......@@ -10,6 +10,7 @@
"redis": "start redis-server --port 6379"
},
"dependencies": {
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@prisma/client": "6.14.0",
......
This diff is collapsed.
......@@ -238,8 +238,8 @@ export async function AddMessage(data: Ty.MessageAddArgs): Promise<Ty.BackendRes
export async function StartAuthSession() : Promise<Ty.BackendResponse<Auth.Session>> {
const sessionId = getRandomToken();
redis.set(`Session:${sessionId}`, "{}");
redis.expire(`Session:${sessionId}`, 60 * 5); // 5分钟内登录/注册有效
redis?.set(`Session:${sessionId}`, "{}");
redis?.expire(`Session:${sessionId}`, 60 * 5); // 5分钟内登录/注册有效
return {
code: 0,
......@@ -252,7 +252,7 @@ export async function StartAuthSession() : Promise<Ty.BackendResponse<Auth.Sessi
export async function Login(args: Auth.AuthArgs) : Promise<Ty.BackendResponse<Auth.LoginResult | null>> {
const { sessionId, username, password, userAgent, userIP } = args;
const sessionStr = await redis.get(`Session:${sessionId}`);
const sessionStr = await redis?.get(`Session:${sessionId}`);
if(!sessionStr)
{
return {
......@@ -289,7 +289,7 @@ export async function Login(args: Auth.AuthArgs) : Promise<Ty.BackendResponse<Au
}
});
await redis.del(`Session:${sessionId}`);
await redis?.del(`Session:${sessionId}`);
return {
code: 0,
......@@ -337,7 +337,7 @@ export async function AutoLogin(args: Auth.AutoLoginArgs) : Promise<Ty.BackendRe
export async function Register(args: Auth.AuthArgs) : Promise<Ty.BackendResponse<Auth.RegisterResult | null>> {
const { sessionId, username, password } = args;
const sessionStr = await redis.get(`Session:${sessionId}`);
const sessionStr = await redis?.get(`Session:${sessionId}`);
if(!sessionStr)
{
return {
......@@ -372,7 +372,7 @@ export async function Register(args: Auth.AuthArgs) : Promise<Ty.BackendResponse
}
});
await redis.del(`Session:${sessionId}`);
await redis?.del(`Session:${sessionId}`);
return {
code: 0,
......
......@@ -6,27 +6,30 @@ import UsernameCheck from './username-check';
import PageHeaderProvider from './header'
import ConfirmModalProvider from './confirm-modal';
import { RoomProvider } from './room-context'
import { Suspense } from 'react';
export default function Home() {
return (
<RoomProvider>
<UsernameCheck />
<ConfirmModalProvider />
<div className="bg-gray-100 flex-col flex h-screen">
<PageHeaderProvider />
<div className="flex flex-1 overflow-hidden">
<div className="w-1/4 bg-white p-4 overflow-y-auto">
<h2 className="text-lg font-bold mb-4">房间列表</h2>
<RoomListProvider />
<Suspense fallback={<div>Loading...</div>}>
<RoomProvider>
<UsernameCheck />
<ConfirmModalProvider />
<div className="bg-gray-100 flex-col flex h-screen">
<PageHeaderProvider />
<div className="flex flex-1 overflow-hidden">
<div className="w-1/4 bg-white p-4 overflow-y-auto">
<h2 className="text-lg font-bold mb-4">房间列表</h2>
<RoomListProvider />
</div>
<div className="w-3/4 bg-gray-100 p-4">
<RoomHeaderProvider />
<RoomMessageProvider />
<SendMessageProvider />
</div>
</div>
</div>
<div className="w-3/4 bg-gray-100 p-4">
<RoomHeaderProvider />
<RoomMessageProvider />
<SendMessageProvider />
</div>
</div>
</div>
</RoomProvider>
</RoomProvider>
</Suspense>
);
}
......@@ -8,14 +8,20 @@ declare global{
var rateLimiter:RateLimiterRedis | undefined;
}
const connectRedisClient = async () => {
const client = createClient();
client.on('error', (err) => console.error('Redis Client Error', err));
await client.connect();
const isBuild = process.env.NEXT_PHASE === 'phase-production-build';
const client = isBuild ? undefined : createClient();
client?.on('error', (err) => console.error('Redis Client Error', err));
await client?.connect();
return client;
};
const getRateLimiter = () => {
const isBuild = process.env.NEXT_PHASE === 'phase-production-build';
if(isBuild) return undefined;
const rateLimiter = new RateLimiterRedis({
storeClient: redis,
keyPrefix: 'rateLimiter',
......@@ -25,6 +31,8 @@ const getRateLimiter = () => {
return rateLimiter;
};
export const prisma = global.prisma || new PrismaClient();
export const redis = global.redisClient || await connectRedisClient();
export const rateLimiter = global.rateLimiter || getRateLimiter();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment