Skip to content

Commit 3cb6a0a

Browse files
夏一飞夏一飞
夏一飞
authored and
夏一飞
committed
build: add Docker setup and update API routes
1 parent fd6bcab commit 3cb6a0a

File tree

11 files changed

+372
-192
lines changed

11 files changed

+372
-192
lines changed

.dockerignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules
2+
.next
3+
.git
4+
.env*
5+
npm-debug.log*
6+
pnpm-debug.log*
7+
.pnpm-store
8+
README.md
9+
.gitignore
10+
.dockerignore
11+
Dockerfile
12+
docker-compose.yml

Dockerfile

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# 使用 Node.js 官方镜像作为基础镜像
2+
FROM node:18-alpine
3+
4+
# 设置工作目录
5+
WORKDIR /app
6+
7+
# 安装必要的系统依赖
8+
RUN apk add --no-cache \
9+
python3 \
10+
make \
11+
g++ \
12+
gcc \
13+
libc-dev \
14+
netcat-openbsd \
15+
postgresql-client
16+
17+
# 全局安装 pnpm
18+
RUN npm install -g pnpm
19+
20+
# 复制 package.json 和 pnpm-lock.yaml
21+
COPY package.json pnpm-lock.yaml ./
22+
23+
# 安装依赖
24+
RUN pnpm install --no-frozen-lockfile
25+
26+
# 复制项目文件
27+
COPY . .
28+
29+
# 添加执行权限到启动脚本
30+
RUN chmod +x start.sh
31+
32+
# 构建应用
33+
RUN pnpm build
34+
35+
# 暴露端口
36+
EXPOSE 3000
37+
38+
# 使用启动脚本
39+
CMD ["./start.sh"]

app/api/users/route.ts

+5-25
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,13 @@
1-
import { sql } from "@vercel/postgres";
2-
import { NextResponse } from "next/server";
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { getUsers } from "@/lib/db";
33

4-
export async function GET(req: Request) {
4+
export async function GET(req: NextRequest) {
55
try {
66
const { searchParams } = new URL(req.url);
77
const page = parseInt(searchParams.get("page") || "1");
8-
const pageSize = 20;
9-
const offset = (page - 1) * pageSize;
8+
const result = await getUsers(page);
109

11-
const countResult = await sql`SELECT COUNT(*) FROM users`;
12-
const total = parseInt(countResult.rows[0].count);
13-
14-
const result = await sql`
15-
SELECT
16-
id,
17-
email,
18-
name,
19-
role,
20-
balance
21-
FROM users
22-
ORDER BY id DESC
23-
LIMIT ${pageSize}
24-
OFFSET ${offset}
25-
`;
26-
27-
return NextResponse.json({
28-
users: result.rows,
29-
total,
30-
});
10+
return NextResponse.json(result);
3111
} catch (error) {
3212
console.error("获取用户列表失败:", error);
3313
return NextResponse.json({ error: "获取用户列表失败" }, { status: 500 });

app/api/v1/models/price/route.ts

+81-112
Original file line numberDiff line numberDiff line change
@@ -9,123 +9,92 @@ interface PriceUpdate {
99

1010
export async function POST(request: NextRequest) {
1111
try {
12-
const body = await request.json();
13-
14-
// 处理单个更新的情况
15-
if (body.id) {
16-
console.log("单个价格更新请求:", {
17-
id: body.id,
18-
input_price: body.input_price,
19-
output_price: body.output_price,
20-
});
21-
22-
const result = await updateModelPrice(
23-
body.id,
24-
body.input_price,
25-
body.output_price
26-
);
27-
28-
if (!result) {
29-
console.log("模型不存在:", body.id);
30-
return NextResponse.json({ error: "模型不存在" }, { status: 404 });
31-
}
32-
33-
return NextResponse.json({
34-
input_price: result.input_price,
35-
output_price: result.output_price,
36-
});
12+
const data = await request.json();
13+
console.log("收到的原始请求数据:", data);
14+
15+
// 从对象中提取模型数组
16+
const updates = data.updates || data;
17+
if (!Array.isArray(updates)) {
18+
console.error("无效的数据格式 - 期望数组:", updates);
19+
return NextResponse.json({ error: "无效的数据格式" }, { status: 400 });
3720
}
3821

39-
// 处理批量更新的情况
40-
if (body.updates && Array.isArray(body.updates)) {
41-
console.log("收到批量更新请求,原始数据:", body.updates);
42-
43-
// 验证并转换每个更新项
44-
const validUpdates = body.updates
45-
.map((update: any) => ({
46-
id: update.id,
47-
input_price: Number(update.input_price),
48-
output_price: Number(update.output_price),
49-
}))
50-
.filter(
51-
(update: PriceUpdate) =>
52-
update.id &&
53-
!isNaN(update.input_price) &&
54-
!isNaN(update.output_price) &&
55-
update.input_price >= 0 &&
56-
update.output_price >= 0
57-
);
58-
59-
console.log("验证后的有效更新数据:", validUpdates);
60-
61-
// 执行批量更新并收集结果
62-
const results = await Promise.all(
63-
validUpdates.map(async (update: PriceUpdate) => {
64-
try {
65-
// console.log("正在更新模型:", update.id, {
66-
// input_price: update.input_price,
67-
// output_price: update.output_price,
68-
// });
69-
70-
const result = await updateModelPrice(
71-
update.id,
72-
update.input_price,
73-
update.output_price
74-
);
75-
76-
// if (!result) {
77-
// console.log("更新失败 - 模型不存在:", update.id);
78-
// } else {
79-
// console.log("更新成功:", {
80-
// id: update.id,
81-
// newPrices: {
82-
// input_price: result.input_price,
83-
// output_price: result.output_price,
84-
// },
85-
// });
86-
// }
87-
88-
return {
89-
id: update.id,
90-
success: !!result,
91-
data: result,
92-
};
93-
} catch (error) {
94-
console.error("更新模型时出错:", update.id, error);
95-
return {
96-
id: update.id,
97-
success: false,
98-
error: error instanceof Error ? error.message : "未知错误",
99-
};
100-
}
101-
})
102-
);
103-
104-
// 过滤出成功更新的记录
105-
const successfulUpdates = results.filter((r) => r.success);
106-
// console.log("成功更新的数量:", successfulUpdates.length);
107-
// console.log("更新结果汇总:", {
108-
// total: results.length,
109-
// successful: successfulUpdates.length,
110-
// failed: results.length - successfulUpdates.length,
111-
// });
112-
113-
return NextResponse.json({
114-
success: true,
115-
updatedCount: successfulUpdates.length,
116-
results: successfulUpdates.map((r) => ({
117-
id: r.id,
118-
input_price: r.data?.input_price,
119-
output_price: r.data?.output_price,
120-
})),
22+
// 验证并转换数据格式
23+
const validUpdates = updates
24+
.map((update: any) => ({
25+
id: update.id,
26+
input_price: Number(update.input_price),
27+
output_price: Number(update.output_price),
28+
}))
29+
.filter((update: PriceUpdate) => {
30+
if (!update.id) {
31+
console.log("跳过 - ID 无效:", update);
32+
return false;
33+
}
34+
35+
if (isNaN(update.input_price) || isNaN(update.output_price)) {
36+
console.log("跳过 - 价格无效:", update);
37+
return false;
38+
}
39+
40+
return true;
12141
});
122-
}
12342

124-
console.log("无效的请求格式:", body);
125-
return NextResponse.json({ error: "无效的请求格式" }, { status: 400 });
43+
console.log("处理后的更新数据:", validUpdates);
44+
console.log(`成功验证 ${validUpdates.length} 个模型的价格更新请求`);
45+
46+
// 执行批量更新并收集结果
47+
const results = await Promise.all(
48+
validUpdates.map(async (update: PriceUpdate) => {
49+
try {
50+
console.log("正在处理模型更新:", {
51+
id: update.id,
52+
input_price: update.input_price,
53+
output_price: update.output_price,
54+
});
55+
56+
const result = await updateModelPrice(
57+
update.id,
58+
update.input_price,
59+
update.output_price
60+
);
61+
62+
console.log("更新结果:", {
63+
id: update.id,
64+
success: !!result,
65+
result,
66+
});
67+
68+
return {
69+
id: update.id,
70+
success: !!result,
71+
data: result,
72+
};
73+
} catch (error) {
74+
console.error("更新失败:", {
75+
id: update.id,
76+
error: error instanceof Error ? error.message : "未知错误",
77+
});
78+
return {
79+
id: update.id,
80+
success: false,
81+
error: error instanceof Error ? error.message : "未知错误",
82+
};
83+
}
84+
})
85+
);
86+
87+
const successCount = results.filter((r) => r.success).length;
88+
console.log(`成功更新 ${successCount} 个模型的价格`);
89+
90+
return NextResponse.json({
91+
success: true,
92+
message: `成功更新 ${successCount} 个模型的价格`,
93+
results,
94+
});
12695
} catch (error) {
127-
console.error("处理请求时发生错误:", error);
128-
return NextResponse.json({ error: "更新价格失败" }, { status: 500 });
96+
console.error("批量更新失败:", error);
97+
return NextResponse.json({ error: "批量更新失败" }, { status: 500 });
12998
}
13099
}
131100

app/api/v1/panel/usage/route.ts

+13-21
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
1-
import { sql } from "@vercel/postgres";
21
import { NextResponse } from "next/server";
3-
import { headers } from "next/headers";
2+
import { Pool } from "pg";
43

5-
export const dynamic = "force-dynamic"; // 禁用路由缓存
6-
export const revalidate = 0; // 禁用数据缓存
4+
const pool = new Pool({
5+
host: process.env.POSTGRES_HOST,
6+
user: process.env.POSTGRES_USER,
7+
password: process.env.POSTGRES_PASSWORD,
8+
database: process.env.POSTGRES_DATABASE,
9+
ssl: false,
10+
});
711

812
export async function GET() {
913
try {
1014
const [modelResult, userResult] = await Promise.all([
11-
sql`
15+
pool.query(`
1216
SELECT
1317
model_name,
1418
COUNT(*) as total_count,
1519
COALESCE(SUM(cost), 0) as total_cost
1620
FROM user_usage_records
1721
GROUP BY model_name
1822
ORDER BY total_cost DESC
19-
`,
20-
sql`
23+
`),
24+
pool.query(`
2125
SELECT
2226
nickname,
2327
COUNT(*) as total_count,
@@ -26,19 +30,9 @@ export async function GET() {
2630
GROUP BY nickname
2731
ORDER BY total_cost DESC
2832
LIMIT 10
29-
`,
33+
`),
3034
]);
3135

32-
// 设置响应头以禁用缓存
33-
const responseHeaders = new Headers();
34-
responseHeaders.set(
35-
"Cache-Control",
36-
"no-store, no-cache, must-revalidate, proxy-revalidate"
37-
);
38-
responseHeaders.set("Pragma", "no-cache");
39-
responseHeaders.set("Expires", "0");
40-
responseHeaders.set("Surrogate-Control", "no-store");
41-
4236
const formattedData = {
4337
models: modelResult.rows.map((row) => ({
4438
model_name: row.model_name,
@@ -52,9 +46,7 @@ export async function GET() {
5246
})),
5347
};
5448

55-
return new NextResponse(JSON.stringify(formattedData), {
56-
headers: responseHeaders,
57-
});
49+
return NextResponse.json(formattedData);
5850
} catch (error) {
5951
console.error("获取使用统计失败:", error);
6052
return NextResponse.json({ error: "获取使用统计失败" }, { status: 500 });

0 commit comments

Comments
 (0)