sa2kit 1.6.91 → 1.6.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client-BlkUL2To.d.ts +26 -0
- package/dist/client-DpMIhrlS.d.mts +26 -0
- package/dist/index-C7yh6b5Q.d.mts +17 -0
- package/dist/index-CDapUIT5.d.mts +51 -0
- package/dist/index-Cv9jlnNz.d.ts +17 -0
- package/dist/index-D3UbkUai.d.ts +51 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1337 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1306 -61
- package/dist/index.mjs.map +1 -1
- package/dist/mikuContest/index.d.mts +13 -0
- package/dist/mikuContest/index.d.ts +13 -0
- package/dist/mikuContest/index.js +1310 -0
- package/dist/mikuContest/index.js.map +1 -0
- package/dist/mikuContest/index.mjs +1253 -0
- package/dist/mikuContest/index.mjs.map +1 -0
- package/dist/mikuContest/logic/index.d.mts +32 -0
- package/dist/mikuContest/logic/index.d.ts +32 -0
- package/dist/mikuContest/logic/index.js +511 -0
- package/dist/mikuContest/logic/index.js.map +1 -0
- package/dist/mikuContest/logic/index.mjs +483 -0
- package/dist/mikuContest/logic/index.mjs.map +1 -0
- package/dist/mikuContest/routes/index.d.mts +80 -0
- package/dist/mikuContest/routes/index.d.ts +80 -0
- package/dist/mikuContest/routes/index.js +821 -0
- package/dist/mikuContest/routes/index.js.map +1 -0
- package/dist/mikuContest/routes/index.mjs +791 -0
- package/dist/mikuContest/routes/index.mjs.map +1 -0
- package/dist/mikuContest/server/index.d.mts +766 -0
- package/dist/mikuContest/server/index.d.ts +766 -0
- package/dist/mikuContest/server/index.js +705 -0
- package/dist/mikuContest/server/index.js.map +1 -0
- package/dist/mikuContest/server/index.mjs +672 -0
- package/dist/mikuContest/server/index.mjs.map +1 -0
- package/dist/mikuContest/service/index.d.mts +30 -0
- package/dist/mikuContest/service/index.d.ts +30 -0
- package/dist/mikuContest/service/index.js +139 -0
- package/dist/mikuContest/service/index.js.map +1 -0
- package/dist/mikuContest/service/index.mjs +135 -0
- package/dist/mikuContest/service/index.mjs.map +1 -0
- package/dist/mikuContest/types/index.d.mts +179 -0
- package/dist/mikuContest/types/index.d.ts +179 -0
- package/dist/mikuContest/types/index.js +4 -0
- package/dist/mikuContest/types/index.js.map +1 -0
- package/dist/mikuContest/types/index.mjs +3 -0
- package/dist/mikuContest/types/index.mjs.map +1 -0
- package/dist/mikuContest/ui/miniapp/index.d.mts +3 -0
- package/dist/mikuContest/ui/miniapp/index.d.ts +3 -0
- package/dist/mikuContest/ui/miniapp/index.js +566 -0
- package/dist/mikuContest/ui/miniapp/index.js.map +1 -0
- package/dist/mikuContest/ui/miniapp/index.mjs +540 -0
- package/dist/mikuContest/ui/miniapp/index.mjs.map +1 -0
- package/dist/mikuContest/ui/web/index.d.mts +4 -0
- package/dist/mikuContest/ui/web/index.d.ts +4 -0
- package/dist/mikuContest/ui/web/index.js +353 -0
- package/dist/mikuContest/ui/web/index.js.map +1 -0
- package/dist/mikuContest/ui/web/index.mjs +343 -0
- package/dist/mikuContest/ui/web/index.mjs.map +1 -0
- package/dist/service-D7DM1wW-.d.ts +38 -0
- package/dist/service-DPr2rlvH.d.mts +38 -0
- package/dist/types-BS7Xz09b.d.mts +14 -0
- package/dist/types-k4koMp4m.d.ts +14 -0
- package/package.json +41 -1
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import * as XLSX from 'xlsx';
|
|
3
|
+
import { pgTable, timestamp, jsonb, text, integer } from 'drizzle-orm/pg-core';
|
|
4
|
+
import { eq } from 'drizzle-orm';
|
|
5
|
+
|
|
6
|
+
// src/mikuContest/routes/index.ts
|
|
7
|
+
|
|
8
|
+
// src/mikuContest/logic/shared/defaults.ts
|
|
9
|
+
var defaultMikuVotingRules = {
|
|
10
|
+
maxVotesPerDay: 3,
|
|
11
|
+
forbidDuplicateVotePerWork: true,
|
|
12
|
+
maxVotesPerDevicePerDay: 20,
|
|
13
|
+
maxVotesPerIpPerDay: 100
|
|
14
|
+
};
|
|
15
|
+
var createDefaultMikuContestConfig = (overrides) => ({
|
|
16
|
+
id: overrides?.id || "miku-contest-default",
|
|
17
|
+
name: overrides?.name || "\u521D\u97F3\u672A\u6765\u793E\u56E2\u5F81\u7A3F\u5927\u8D5B",
|
|
18
|
+
theme: overrides?.theme || "\u521D\u97F3\u672A\u6765\u4E3B\u9898\u521B\u4F5C\u5F81\u7A3F",
|
|
19
|
+
organizer: overrides?.organizer || "\u521D\u97F3\u672A\u6765\u793E\u56E2",
|
|
20
|
+
awards: overrides?.awards || ["\u4E00\u7B49\u5956", "\u4E8C\u7B49\u5956", "\u4E09\u7B49\u5956", "\u4EBA\u6C14\u5956"],
|
|
21
|
+
rules: overrides?.rules || "\u8BF7\u786E\u4FDD\u4F5C\u54C1\u539F\u521B\u4E14\u7B26\u5408\u793E\u56E2\u89C4\u8303\u3002",
|
|
22
|
+
copyright: overrides?.copyright || "\u6295\u7A3F\u5373\u89C6\u4E3A\u6388\u6743\u8D5B\u4E8B\u5C55\u793A\u4E0E\u516C\u793A\u3002",
|
|
23
|
+
timeline: overrides?.timeline || {
|
|
24
|
+
submissionStartAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25
|
+
submissionEndAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26
|
+
votingStartAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27
|
+
votingEndAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28
|
+
publicResultAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
29
|
+
},
|
|
30
|
+
votingRules: {
|
|
31
|
+
...defaultMikuVotingRules,
|
|
32
|
+
...overrides?.votingRules || {}
|
|
33
|
+
},
|
|
34
|
+
toggles: {
|
|
35
|
+
submissionEnabled: overrides?.toggles?.submissionEnabled ?? true,
|
|
36
|
+
votingEnabled: overrides?.toggles?.votingEnabled ?? true,
|
|
37
|
+
resultEnabled: overrides?.toggles?.resultEnabled ?? false
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// src/mikuContest/logic/shared/validators.ts
|
|
42
|
+
var DESCRIPTION_LIMIT = 500;
|
|
43
|
+
var MINIAPP_DESCRIPTION_LIMIT = 200;
|
|
44
|
+
var TEXT_CONTENT_LIMIT = 2e3;
|
|
45
|
+
var MAX_TAGS = 3;
|
|
46
|
+
var hasValue = (value) => {
|
|
47
|
+
return typeof value === "string" ? value.trim().length > 0 : value !== null && value !== void 0;
|
|
48
|
+
};
|
|
49
|
+
var validateByType = (type, input) => {
|
|
50
|
+
const errors = [];
|
|
51
|
+
const { content } = input;
|
|
52
|
+
switch (type) {
|
|
53
|
+
case "visual": {
|
|
54
|
+
const imageCount = content.images?.length || 0;
|
|
55
|
+
if (imageCount < 1 || imageCount > 3) {
|
|
56
|
+
errors.push("\u89C6\u89C9\u7C7B\u4F5C\u54C1\u9700\u4E0A\u4F20 1-3 \u5F20\u56FE\u7247");
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case "video": {
|
|
61
|
+
if (!hasValue(content.videoLink)) {
|
|
62
|
+
errors.push("\u89C6\u9891\u7C7B\u4F5C\u54C1\u9700\u63D0\u4F9B\u89C6\u9891\u94FE\u63A5");
|
|
63
|
+
}
|
|
64
|
+
if (!hasValue(content.coverImage)) {
|
|
65
|
+
errors.push("\u89C6\u9891\u7C7B\u4F5C\u54C1\u9700\u63D0\u4F9B\u5C01\u9762\u56FE");
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "text": {
|
|
70
|
+
const text2 = content.textContent || "";
|
|
71
|
+
if (!text2.trim()) {
|
|
72
|
+
errors.push("\u6587\u5B57\u7C7B\u4F5C\u54C1\u9700\u586B\u5199\u6B63\u6587");
|
|
73
|
+
}
|
|
74
|
+
if (text2.length > TEXT_CONTENT_LIMIT) {
|
|
75
|
+
errors.push(`\u6587\u5B57\u6B63\u6587\u4E0D\u80FD\u8D85\u8FC7 ${TEXT_CONTENT_LIMIT} \u5B57`);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case "audio": {
|
|
80
|
+
if (!hasValue(content.audioLink)) {
|
|
81
|
+
errors.push("\u97F3\u9891\u7C7B\u4F5C\u54C1\u9700\u63D0\u4F9B\u97F3\u9891\u94FE\u63A5");
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return errors;
|
|
87
|
+
};
|
|
88
|
+
var validateMikuSubmissionInput = (input, mode = "web") => {
|
|
89
|
+
const errors = [];
|
|
90
|
+
if (!input.contestId.trim()) errors.push("contestId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
91
|
+
if (!input.authorId.trim()) errors.push("authorId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
92
|
+
if (!input.authorNickname.trim()) errors.push("\u4F5C\u8005\u6635\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
93
|
+
if (!input.title.trim()) errors.push("\u4F5C\u54C1\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
94
|
+
const descriptionLimit = mode === "miniapp" ? MINIAPP_DESCRIPTION_LIMIT : DESCRIPTION_LIMIT;
|
|
95
|
+
if (input.description.length > descriptionLimit) {
|
|
96
|
+
errors.push(`\u4F5C\u54C1\u7B80\u4ECB\u4E0D\u80FD\u8D85\u8FC7 ${descriptionLimit} \u5B57`);
|
|
97
|
+
}
|
|
98
|
+
if ((input.tags?.length || 0) > MAX_TAGS) {
|
|
99
|
+
errors.push(`\u6807\u7B7E\u6700\u591A ${MAX_TAGS} \u4E2A`);
|
|
100
|
+
}
|
|
101
|
+
errors.push(...validateByType(input.type, input));
|
|
102
|
+
return errors;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/mikuContest/logic/shared/voting.ts
|
|
106
|
+
var toVoteDayKey = (date = /* @__PURE__ */ new Date()) => {
|
|
107
|
+
const y = date.getUTCFullYear();
|
|
108
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
109
|
+
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
110
|
+
return `${y}-${m}-${d}`;
|
|
111
|
+
};
|
|
112
|
+
var checkVoteEligibility = (context) => {
|
|
113
|
+
const { existingVotes, submissionId, voterId, dayKey, rules } = context;
|
|
114
|
+
const userTodayVotes = existingVotes.filter((vote) => vote.voterId === voterId && vote.dayKey === dayKey);
|
|
115
|
+
if (userTodayVotes.length >= rules.maxVotesPerDay) {
|
|
116
|
+
return { ok: false, reason: "\u5DF2\u8FBE\u5230\u4ECA\u65E5\u6295\u7968\u4E0A\u9650" };
|
|
117
|
+
}
|
|
118
|
+
if (rules.forbidDuplicateVotePerWork) {
|
|
119
|
+
const duplicated = userTodayVotes.some((vote) => vote.submissionId === submissionId);
|
|
120
|
+
if (duplicated) return { ok: false, reason: "\u4E0D\u53EF\u91CD\u590D\u6295\u540C\u4E00\u4F5C\u54C1" };
|
|
121
|
+
}
|
|
122
|
+
return { ok: true };
|
|
123
|
+
};
|
|
124
|
+
var sortByVotesDesc = (items) => {
|
|
125
|
+
return [...items].sort((a, b) => {
|
|
126
|
+
if (b.voteCount === a.voteCount) {
|
|
127
|
+
return (a.createdAt || "").localeCompare(b.createdAt || "");
|
|
128
|
+
}
|
|
129
|
+
return b.voteCount - a.voteCount;
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
var randomId = (prefix) => {
|
|
133
|
+
return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
134
|
+
};
|
|
135
|
+
var serialNo = () => {
|
|
136
|
+
const now = /* @__PURE__ */ new Date();
|
|
137
|
+
const y = now.getFullYear();
|
|
138
|
+
const m = String(now.getMonth() + 1).padStart(2, "0");
|
|
139
|
+
const d = String(now.getDate()).padStart(2, "0");
|
|
140
|
+
const seq = Math.floor(Math.random() * 9e3 + 1e3);
|
|
141
|
+
return `MIKU-${y}${m}${d}-${seq}`;
|
|
142
|
+
};
|
|
143
|
+
var MikuContestService = class {
|
|
144
|
+
constructor(options = {}) {
|
|
145
|
+
this.submissions = /* @__PURE__ */ new Map();
|
|
146
|
+
this.votes = [];
|
|
147
|
+
this.announcements = /* @__PURE__ */ new Map();
|
|
148
|
+
this.voterRestrictions = /* @__PURE__ */ new Map();
|
|
149
|
+
this.contest = createDefaultMikuContestConfig(options.contestConfig);
|
|
150
|
+
}
|
|
151
|
+
getContestConfig() {
|
|
152
|
+
return this.contest;
|
|
153
|
+
}
|
|
154
|
+
updateContestConfig(patch) {
|
|
155
|
+
this.contest = {
|
|
156
|
+
...this.contest,
|
|
157
|
+
...patch,
|
|
158
|
+
votingRules: {
|
|
159
|
+
...this.contest.votingRules,
|
|
160
|
+
...patch.votingRules || {}
|
|
161
|
+
},
|
|
162
|
+
toggles: {
|
|
163
|
+
...this.contest.toggles,
|
|
164
|
+
...patch.toggles || {}
|
|
165
|
+
},
|
|
166
|
+
timeline: {
|
|
167
|
+
...this.contest.timeline,
|
|
168
|
+
...patch.timeline || {}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
return this.contest;
|
|
172
|
+
}
|
|
173
|
+
createSubmission(input, mode = "web") {
|
|
174
|
+
if (!this.contest.toggles.submissionEnabled) {
|
|
175
|
+
throw new Error("\u5F53\u524D\u672A\u5F00\u653E\u6295\u7A3F");
|
|
176
|
+
}
|
|
177
|
+
const errors = validateMikuSubmissionInput(input, mode);
|
|
178
|
+
if (errors.length > 0) {
|
|
179
|
+
throw new Error(errors.join("\uFF1B"));
|
|
180
|
+
}
|
|
181
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
182
|
+
const next = {
|
|
183
|
+
id: randomId("submission"),
|
|
184
|
+
serialNo: serialNo(),
|
|
185
|
+
contestId: input.contestId,
|
|
186
|
+
authorId: input.authorId,
|
|
187
|
+
authorNickname: input.authorNickname,
|
|
188
|
+
title: input.title,
|
|
189
|
+
type: input.type,
|
|
190
|
+
description: input.description,
|
|
191
|
+
tags: input.tags || [],
|
|
192
|
+
content: input.content,
|
|
193
|
+
voteCount: 0,
|
|
194
|
+
status: "pending",
|
|
195
|
+
createdAt: now,
|
|
196
|
+
updatedAt: now
|
|
197
|
+
};
|
|
198
|
+
this.submissions.set(next.id, next);
|
|
199
|
+
return next;
|
|
200
|
+
}
|
|
201
|
+
listSubmissions(filter) {
|
|
202
|
+
const authorKeyword = filter?.authorKeyword?.trim().toLowerCase();
|
|
203
|
+
const titleKeyword = filter?.titleKeyword?.trim().toLowerCase();
|
|
204
|
+
return [...this.submissions.values()].filter((item) => {
|
|
205
|
+
if (filter?.status && item.status !== filter.status) return false;
|
|
206
|
+
if (filter?.type && item.type !== filter.type) return false;
|
|
207
|
+
if (filter?.authorId && item.authorId !== filter.authorId) return false;
|
|
208
|
+
if (authorKeyword && !item.authorNickname.toLowerCase().includes(authorKeyword)) return false;
|
|
209
|
+
if (titleKeyword && !item.title.toLowerCase().includes(titleKeyword)) return false;
|
|
210
|
+
return true;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
getSubmission(submissionId) {
|
|
214
|
+
return this.submissions.get(submissionId) || null;
|
|
215
|
+
}
|
|
216
|
+
reviewSubmission(input) {
|
|
217
|
+
const current = this.submissions.get(input.submissionId);
|
|
218
|
+
if (!current) throw new Error("\u6295\u7A3F\u4E0D\u5B58\u5728");
|
|
219
|
+
if (input.action === "reject" && !input.rejectReason?.trim()) {
|
|
220
|
+
throw new Error("\u9A73\u56DE\u9700\u586B\u5199\u539F\u56E0");
|
|
221
|
+
}
|
|
222
|
+
const reviewed = {
|
|
223
|
+
...current,
|
|
224
|
+
status: input.action === "approve" ? "approved" : "rejected",
|
|
225
|
+
rejectReason: input.action === "reject" ? input.rejectReason?.trim() : void 0,
|
|
226
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
227
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
228
|
+
};
|
|
229
|
+
this.submissions.set(reviewed.id, reviewed);
|
|
230
|
+
return reviewed;
|
|
231
|
+
}
|
|
232
|
+
vote(input) {
|
|
233
|
+
if (!this.contest.toggles.votingEnabled) {
|
|
234
|
+
throw new Error("\u5F53\u524D\u672A\u5F00\u653E\u6295\u7968");
|
|
235
|
+
}
|
|
236
|
+
const restriction = this.voterRestrictions.get(input.voterId);
|
|
237
|
+
if (restriction?.banned) {
|
|
238
|
+
throw new Error("\u5F53\u524D\u8D26\u53F7\u5DF2\u88AB\u9650\u5236\u6295\u7968");
|
|
239
|
+
}
|
|
240
|
+
const target = this.submissions.get(input.submissionId);
|
|
241
|
+
if (!target) throw new Error("\u4F5C\u54C1\u4E0D\u5B58\u5728");
|
|
242
|
+
if (target.status !== "approved") throw new Error("\u4EC5\u53EF\u5BF9\u5DF2\u8FC7\u5BA1\u4F5C\u54C1\u6295\u7968");
|
|
243
|
+
const dayKey = toVoteDayKey();
|
|
244
|
+
const eligible = checkVoteEligibility({
|
|
245
|
+
existingVotes: this.votes,
|
|
246
|
+
submissionId: input.submissionId,
|
|
247
|
+
voterId: input.voterId,
|
|
248
|
+
dayKey,
|
|
249
|
+
rules: this.contest.votingRules
|
|
250
|
+
});
|
|
251
|
+
if (!eligible.ok) {
|
|
252
|
+
throw new Error(eligible.reason || "\u6295\u7968\u5931\u8D25");
|
|
253
|
+
}
|
|
254
|
+
const vote = {
|
|
255
|
+
id: randomId("vote"),
|
|
256
|
+
contestId: input.contestId,
|
|
257
|
+
submissionId: input.submissionId,
|
|
258
|
+
voterId: input.voterId,
|
|
259
|
+
votedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
260
|
+
dayKey,
|
|
261
|
+
deviceId: input.deviceId,
|
|
262
|
+
ip: input.ip
|
|
263
|
+
};
|
|
264
|
+
this.votes.push(vote);
|
|
265
|
+
const updated = {
|
|
266
|
+
...target,
|
|
267
|
+
voteCount: target.voteCount + 1,
|
|
268
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
269
|
+
};
|
|
270
|
+
this.submissions.set(updated.id, updated);
|
|
271
|
+
return updated;
|
|
272
|
+
}
|
|
273
|
+
getVoterRestriction(voterId) {
|
|
274
|
+
return this.voterRestrictions.get(voterId) || null;
|
|
275
|
+
}
|
|
276
|
+
setVoterRestriction(input) {
|
|
277
|
+
const next = {
|
|
278
|
+
voterId: input.voterId,
|
|
279
|
+
banned: input.banned,
|
|
280
|
+
reason: input.reason,
|
|
281
|
+
operatorId: input.operatorId,
|
|
282
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
283
|
+
};
|
|
284
|
+
this.voterRestrictions.set(input.voterId, next);
|
|
285
|
+
return next;
|
|
286
|
+
}
|
|
287
|
+
resetVotes(input) {
|
|
288
|
+
if (!input.submissionId && !input.voterId) {
|
|
289
|
+
throw new Error("submissionId \u4E0E voterId \u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A");
|
|
290
|
+
}
|
|
291
|
+
const before = this.votes.length;
|
|
292
|
+
const affected = /* @__PURE__ */ new Set();
|
|
293
|
+
const remained = this.votes.filter((vote) => {
|
|
294
|
+
const matchSubmission = input.submissionId ? vote.submissionId === input.submissionId : true;
|
|
295
|
+
const matchVoter = input.voterId ? vote.voterId === input.voterId : true;
|
|
296
|
+
const shouldRemove = matchSubmission && matchVoter;
|
|
297
|
+
if (shouldRemove) affected.add(vote.submissionId);
|
|
298
|
+
return !shouldRemove;
|
|
299
|
+
});
|
|
300
|
+
this.votes.length = 0;
|
|
301
|
+
this.votes.push(...remained);
|
|
302
|
+
this.recalculateVoteCounts();
|
|
303
|
+
return {
|
|
304
|
+
removedVotes: before - remained.length,
|
|
305
|
+
affectedSubmissions: [...affected]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
listAnnouncements(contestId) {
|
|
309
|
+
const all = [...this.announcements.values()];
|
|
310
|
+
return contestId ? all.filter((item) => item.contestId === contestId) : all;
|
|
311
|
+
}
|
|
312
|
+
publishAnnouncement(input) {
|
|
313
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
314
|
+
const announcement = {
|
|
315
|
+
id: randomId("notice"),
|
|
316
|
+
contestId: input.contestId,
|
|
317
|
+
title: input.title,
|
|
318
|
+
content: input.content,
|
|
319
|
+
type: input.type,
|
|
320
|
+
createdBy: input.createdBy,
|
|
321
|
+
createdAt: now,
|
|
322
|
+
updatedAt: now
|
|
323
|
+
};
|
|
324
|
+
this.announcements.set(announcement.id, announcement);
|
|
325
|
+
return announcement;
|
|
326
|
+
}
|
|
327
|
+
getLeaderboard(limit = 10) {
|
|
328
|
+
const ranked = sortByVotesDesc(this.listSubmissions({ status: "approved" })).slice(0, limit);
|
|
329
|
+
return ranked.map((item, index) => ({
|
|
330
|
+
submissionId: item.id,
|
|
331
|
+
title: item.title,
|
|
332
|
+
authorNickname: item.authorNickname,
|
|
333
|
+
voteCount: item.voteCount,
|
|
334
|
+
rank: index + 1
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
337
|
+
getSnapshot() {
|
|
338
|
+
return {
|
|
339
|
+
contest: this.contest,
|
|
340
|
+
submissions: this.listSubmissions(),
|
|
341
|
+
announcements: this.listAnnouncements(),
|
|
342
|
+
leaderboard: this.getLeaderboard()
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
getSubmissionExportRows(filter) {
|
|
346
|
+
return this.listSubmissions(filter).map((item) => ({
|
|
347
|
+
\u6295\u7A3F\u7F16\u53F7: item.serialNo,
|
|
348
|
+
\u6295\u7A3FID: item.id,
|
|
349
|
+
\u8D5B\u4E8BID: item.contestId,
|
|
350
|
+
\u4F5C\u8005ID: item.authorId,
|
|
351
|
+
\u4F5C\u8005\u6635\u79F0: item.authorNickname,
|
|
352
|
+
\u4F5C\u54C1\u540D\u79F0: item.title,
|
|
353
|
+
\u4F5C\u54C1\u7C7B\u578B: item.type,
|
|
354
|
+
\u7B80\u4ECB: item.description,
|
|
355
|
+
\u6807\u7B7E: item.tags.join(","),
|
|
356
|
+
\u5BA1\u6838\u72B6\u6001: item.status,
|
|
357
|
+
\u9A73\u56DE\u539F\u56E0: item.rejectReason || "",
|
|
358
|
+
\u7968\u6570: item.voteCount,
|
|
359
|
+
\u63D0\u4EA4\u65F6\u95F4: item.createdAt,
|
|
360
|
+
\u66F4\u65B0\u65F6\u95F4: item.updatedAt
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
exportSubmissionExcel(filter) {
|
|
364
|
+
const rows = this.getSubmissionExportRows(filter);
|
|
365
|
+
const workbook = XLSX.utils.book_new();
|
|
366
|
+
const worksheet = XLSX.utils.json_to_sheet(rows);
|
|
367
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, "submissions");
|
|
368
|
+
return XLSX.write(workbook, { bookType: "xlsx", type: "buffer" });
|
|
369
|
+
}
|
|
370
|
+
recalculateVoteCounts() {
|
|
371
|
+
const counts = /* @__PURE__ */ new Map();
|
|
372
|
+
for (const vote of this.votes) {
|
|
373
|
+
counts.set(vote.submissionId, (counts.get(vote.submissionId) || 0) + 1);
|
|
374
|
+
}
|
|
375
|
+
for (const [id, submission] of this.submissions.entries()) {
|
|
376
|
+
const nextCount = counts.get(id) || 0;
|
|
377
|
+
if (submission.voteCount === nextCount) continue;
|
|
378
|
+
this.submissions.set(id, {
|
|
379
|
+
...submission,
|
|
380
|
+
voteCount: nextCount,
|
|
381
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
exportPersistenceState() {
|
|
386
|
+
return {
|
|
387
|
+
contest: this.contest,
|
|
388
|
+
submissions: [...this.submissions.values()],
|
|
389
|
+
votes: [...this.votes],
|
|
390
|
+
announcements: [...this.announcements.values()],
|
|
391
|
+
voterRestrictions: [...this.voterRestrictions.values()],
|
|
392
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
importPersistenceState(state) {
|
|
396
|
+
this.contest = state.contest;
|
|
397
|
+
this.submissions.clear();
|
|
398
|
+
this.announcements.clear();
|
|
399
|
+
this.voterRestrictions.clear();
|
|
400
|
+
this.votes.length = 0;
|
|
401
|
+
for (const item of state.submissions) {
|
|
402
|
+
this.submissions.set(item.id, item);
|
|
403
|
+
}
|
|
404
|
+
for (const item of state.announcements) {
|
|
405
|
+
this.announcements.set(item.id, item);
|
|
406
|
+
}
|
|
407
|
+
for (const item of state.voterRestrictions) {
|
|
408
|
+
this.voterRestrictions.set(item.voterId, item);
|
|
409
|
+
}
|
|
410
|
+
this.votes.push(...state.votes);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
var createMikuContestService = (options) => {
|
|
414
|
+
return new MikuContestService(options);
|
|
415
|
+
};
|
|
416
|
+
var mikuContestConfigs = pgTable("miku_contest_configs", {
|
|
417
|
+
contestId: text("contest_id").primaryKey(),
|
|
418
|
+
config: jsonb("config").$type().notNull(),
|
|
419
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
420
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
421
|
+
});
|
|
422
|
+
var mikuContestSubmissions = pgTable("miku_contest_submissions", {
|
|
423
|
+
id: text("id").primaryKey(),
|
|
424
|
+
contestId: text("contest_id").notNull(),
|
|
425
|
+
serialNo: text("serial_no").notNull(),
|
|
426
|
+
authorId: text("author_id").notNull(),
|
|
427
|
+
authorNickname: text("author_nickname").notNull(),
|
|
428
|
+
title: text("title").notNull(),
|
|
429
|
+
type: text("type").notNull(),
|
|
430
|
+
description: text("description").notNull(),
|
|
431
|
+
tags: jsonb("tags").$type().notNull(),
|
|
432
|
+
content: jsonb("content").$type().notNull(),
|
|
433
|
+
voteCount: integer("vote_count").notNull().default(0),
|
|
434
|
+
status: text("status").notNull(),
|
|
435
|
+
rejectReason: text("reject_reason"),
|
|
436
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull(),
|
|
437
|
+
reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
|
|
438
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull()
|
|
439
|
+
});
|
|
440
|
+
var mikuContestVotes = pgTable("miku_contest_votes", {
|
|
441
|
+
id: text("id").primaryKey(),
|
|
442
|
+
contestId: text("contest_id").notNull(),
|
|
443
|
+
submissionId: text("submission_id").notNull(),
|
|
444
|
+
voterId: text("voter_id").notNull(),
|
|
445
|
+
votedAt: text("voted_at").notNull(),
|
|
446
|
+
dayKey: text("day_key").notNull(),
|
|
447
|
+
deviceId: text("device_id"),
|
|
448
|
+
ip: text("ip")
|
|
449
|
+
});
|
|
450
|
+
var mikuContestNotices = pgTable("miku_contest_notices", {
|
|
451
|
+
id: text("id").primaryKey(),
|
|
452
|
+
contestId: text("contest_id").notNull(),
|
|
453
|
+
title: text("title").notNull(),
|
|
454
|
+
content: text("content").notNull(),
|
|
455
|
+
type: text("type").notNull(),
|
|
456
|
+
createdBy: text("created_by").notNull(),
|
|
457
|
+
createdAt: text("created_at").notNull(),
|
|
458
|
+
updatedAt: text("updated_at").notNull()
|
|
459
|
+
});
|
|
460
|
+
var mikuContestVoterRestrictions = pgTable("miku_contest_voter_restrictions", {
|
|
461
|
+
id: text("id").primaryKey(),
|
|
462
|
+
contestId: text("contest_id").notNull(),
|
|
463
|
+
data: jsonb("data").$type().notNull()
|
|
464
|
+
});
|
|
465
|
+
var MikuContestStateDbService = class {
|
|
466
|
+
constructor(db) {
|
|
467
|
+
this.db = db;
|
|
468
|
+
}
|
|
469
|
+
async loadState(contestId) {
|
|
470
|
+
const configRows = await this.db.select({ config: mikuContestConfigs.config }).from(mikuContestConfigs).where(eq(mikuContestConfigs.contestId, contestId)).limit(1);
|
|
471
|
+
const config = configRows[0]?.config;
|
|
472
|
+
if (!config) return null;
|
|
473
|
+
const [submissions, votes, announcements, restrictions] = await Promise.all([
|
|
474
|
+
this.db.select().from(mikuContestSubmissions).where(eq(mikuContestSubmissions.contestId, contestId)),
|
|
475
|
+
this.db.select().from(mikuContestVotes).where(eq(mikuContestVotes.contestId, contestId)),
|
|
476
|
+
this.db.select().from(mikuContestNotices).where(eq(mikuContestNotices.contestId, contestId)),
|
|
477
|
+
this.db.select({ data: mikuContestVoterRestrictions.data }).from(mikuContestVoterRestrictions).where(eq(mikuContestVoterRestrictions.contestId, contestId))
|
|
478
|
+
]);
|
|
479
|
+
return {
|
|
480
|
+
contest: config,
|
|
481
|
+
submissions,
|
|
482
|
+
votes,
|
|
483
|
+
announcements,
|
|
484
|
+
voterRestrictions: restrictions.map((item) => item.data),
|
|
485
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
async saveState(state) {
|
|
489
|
+
const contestId = state.contest.id;
|
|
490
|
+
const exists = await this.db.select({ contestId: mikuContestConfigs.contestId }).from(mikuContestConfigs).where(eq(mikuContestConfigs.contestId, contestId)).limit(1);
|
|
491
|
+
if (exists[0]) {
|
|
492
|
+
await this.db.update(mikuContestConfigs).set({
|
|
493
|
+
config: state.contest,
|
|
494
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
495
|
+
}).where(eq(mikuContestConfigs.contestId, contestId));
|
|
496
|
+
} else {
|
|
497
|
+
await this.db.insert(mikuContestConfigs).values({
|
|
498
|
+
contestId,
|
|
499
|
+
config: state.contest
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
await this.db.delete(mikuContestSubmissions).where(eq(mikuContestSubmissions.contestId, contestId));
|
|
503
|
+
await this.db.delete(mikuContestVotes).where(eq(mikuContestVotes.contestId, contestId));
|
|
504
|
+
await this.db.delete(mikuContestNotices).where(eq(mikuContestNotices.contestId, contestId));
|
|
505
|
+
await this.db.delete(mikuContestVoterRestrictions).where(eq(mikuContestVoterRestrictions.contestId, contestId));
|
|
506
|
+
if (state.submissions.length > 0) {
|
|
507
|
+
await this.db.insert(mikuContestSubmissions).values(
|
|
508
|
+
state.submissions.map((item) => ({
|
|
509
|
+
...item,
|
|
510
|
+
contestId,
|
|
511
|
+
createdAt: new Date(item.createdAt),
|
|
512
|
+
reviewedAt: item.reviewedAt ? new Date(item.reviewedAt) : null,
|
|
513
|
+
updatedAt: new Date(item.updatedAt)
|
|
514
|
+
}))
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
if (state.votes.length > 0) {
|
|
518
|
+
await this.db.insert(mikuContestVotes).values(state.votes);
|
|
519
|
+
}
|
|
520
|
+
if (state.announcements.length > 0) {
|
|
521
|
+
await this.db.insert(mikuContestNotices).values(state.announcements);
|
|
522
|
+
}
|
|
523
|
+
if (state.voterRestrictions.length > 0) {
|
|
524
|
+
await this.db.insert(mikuContestVoterRestrictions).values(
|
|
525
|
+
state.voterRestrictions.map((item) => ({
|
|
526
|
+
id: `${contestId}:${item.voterId}`,
|
|
527
|
+
contestId,
|
|
528
|
+
data: item
|
|
529
|
+
}))
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// src/mikuContest/server/persistence/drizzle-adapter.ts
|
|
536
|
+
var createMikuContestDrizzlePersistenceAdapter = (db) => {
|
|
537
|
+
const service = new MikuContestStateDbService(db);
|
|
538
|
+
return {
|
|
539
|
+
loadState: (contestId) => service.loadState(contestId),
|
|
540
|
+
saveState: (state) => service.saveState(state)
|
|
541
|
+
};
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// src/mikuContest/server/persistence/service.ts
|
|
545
|
+
var MikuContestPersistentService = class {
|
|
546
|
+
constructor(options) {
|
|
547
|
+
this.options = options;
|
|
548
|
+
this.hydrated = false;
|
|
549
|
+
this.hydrationPromise = null;
|
|
550
|
+
this.engine = createMikuContestService(options);
|
|
551
|
+
}
|
|
552
|
+
async ensureHydrated() {
|
|
553
|
+
if (this.hydrated) return;
|
|
554
|
+
if (this.hydrationPromise) return this.hydrationPromise;
|
|
555
|
+
this.hydrationPromise = (async () => {
|
|
556
|
+
const contestId = this.engine.getContestConfig().id;
|
|
557
|
+
const loaded = await this.options.persistenceAdapter.loadState(contestId);
|
|
558
|
+
if (loaded) {
|
|
559
|
+
this.engine.importPersistenceState(loaded);
|
|
560
|
+
} else {
|
|
561
|
+
await this.options.persistenceAdapter.saveState(this.engine.exportPersistenceState());
|
|
562
|
+
}
|
|
563
|
+
this.hydrated = true;
|
|
564
|
+
})();
|
|
565
|
+
await this.hydrationPromise;
|
|
566
|
+
}
|
|
567
|
+
async persist() {
|
|
568
|
+
await this.options.persistenceAdapter.saveState(this.engine.exportPersistenceState());
|
|
569
|
+
}
|
|
570
|
+
async getContestConfig() {
|
|
571
|
+
await this.ensureHydrated();
|
|
572
|
+
return this.engine.getContestConfig();
|
|
573
|
+
}
|
|
574
|
+
async updateContestConfig(patch) {
|
|
575
|
+
await this.ensureHydrated();
|
|
576
|
+
const data = this.engine.updateContestConfig(patch);
|
|
577
|
+
await this.persist();
|
|
578
|
+
return data;
|
|
579
|
+
}
|
|
580
|
+
async createSubmission(input, mode = "web") {
|
|
581
|
+
await this.ensureHydrated();
|
|
582
|
+
const data = this.engine.createSubmission(input, mode);
|
|
583
|
+
await this.persist();
|
|
584
|
+
return data;
|
|
585
|
+
}
|
|
586
|
+
async listSubmissions(filter) {
|
|
587
|
+
await this.ensureHydrated();
|
|
588
|
+
return this.engine.listSubmissions(filter);
|
|
589
|
+
}
|
|
590
|
+
async getSubmission(submissionId) {
|
|
591
|
+
await this.ensureHydrated();
|
|
592
|
+
return this.engine.getSubmission(submissionId);
|
|
593
|
+
}
|
|
594
|
+
async reviewSubmission(input) {
|
|
595
|
+
await this.ensureHydrated();
|
|
596
|
+
const data = this.engine.reviewSubmission(input);
|
|
597
|
+
await this.persist();
|
|
598
|
+
return data;
|
|
599
|
+
}
|
|
600
|
+
async vote(input) {
|
|
601
|
+
await this.ensureHydrated();
|
|
602
|
+
const data = this.engine.vote(input);
|
|
603
|
+
await this.persist();
|
|
604
|
+
return data;
|
|
605
|
+
}
|
|
606
|
+
async getVoterRestriction(voterId) {
|
|
607
|
+
await this.ensureHydrated();
|
|
608
|
+
return this.engine.getVoterRestriction(voterId);
|
|
609
|
+
}
|
|
610
|
+
async setVoterRestriction(input) {
|
|
611
|
+
await this.ensureHydrated();
|
|
612
|
+
const data = this.engine.setVoterRestriction(input);
|
|
613
|
+
await this.persist();
|
|
614
|
+
return data;
|
|
615
|
+
}
|
|
616
|
+
async resetVotes(input) {
|
|
617
|
+
await this.ensureHydrated();
|
|
618
|
+
const data = this.engine.resetVotes(input);
|
|
619
|
+
await this.persist();
|
|
620
|
+
return data;
|
|
621
|
+
}
|
|
622
|
+
async listAnnouncements(contestId) {
|
|
623
|
+
await this.ensureHydrated();
|
|
624
|
+
return this.engine.listAnnouncements(contestId);
|
|
625
|
+
}
|
|
626
|
+
async publishAnnouncement(input) {
|
|
627
|
+
await this.ensureHydrated();
|
|
628
|
+
const data = this.engine.publishAnnouncement(input);
|
|
629
|
+
await this.persist();
|
|
630
|
+
return data;
|
|
631
|
+
}
|
|
632
|
+
async getLeaderboard(limit = 10) {
|
|
633
|
+
await this.ensureHydrated();
|
|
634
|
+
return this.engine.getLeaderboard(limit);
|
|
635
|
+
}
|
|
636
|
+
async getSnapshot() {
|
|
637
|
+
await this.ensureHydrated();
|
|
638
|
+
return this.engine.getSnapshot();
|
|
639
|
+
}
|
|
640
|
+
async getSubmissionExportRows(filter) {
|
|
641
|
+
await this.ensureHydrated();
|
|
642
|
+
return this.engine.getSubmissionExportRows(filter);
|
|
643
|
+
}
|
|
644
|
+
async exportSubmissionExcel(filter) {
|
|
645
|
+
await this.ensureHydrated();
|
|
646
|
+
return this.engine.exportSubmissionExcel(filter);
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
var createMikuContestPersistentService = (options) => {
|
|
650
|
+
return new MikuContestPersistentService(options);
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// src/mikuContest/routes/index.ts
|
|
654
|
+
var isDrizzleDb = (value) => {
|
|
655
|
+
if (!value || typeof value !== "object") return false;
|
|
656
|
+
const candidate = value;
|
|
657
|
+
return typeof candidate.select === "function" && typeof candidate.insert === "function" && typeof candidate.update === "function" && typeof candidate.delete === "function";
|
|
658
|
+
};
|
|
659
|
+
var resolveService = (config) => {
|
|
660
|
+
if (config?.service) return config.service;
|
|
661
|
+
const adapter = config?.persistenceAdapter || (isDrizzleDb(config?.db) ? createMikuContestDrizzlePersistenceAdapter(config.db) : null);
|
|
662
|
+
if (adapter) {
|
|
663
|
+
return createMikuContestPersistentService({
|
|
664
|
+
persistenceAdapter: adapter
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
return createMikuContestService();
|
|
668
|
+
};
|
|
669
|
+
var createGetContestSnapshotHandler = (config) => {
|
|
670
|
+
const service = resolveService(config);
|
|
671
|
+
return async (_request) => {
|
|
672
|
+
const data = await service.getSnapshot();
|
|
673
|
+
return NextResponse.json({ success: true, data });
|
|
674
|
+
};
|
|
675
|
+
};
|
|
676
|
+
var createUpdateContestConfigHandler = (config) => {
|
|
677
|
+
const service = resolveService(config);
|
|
678
|
+
return async (request) => {
|
|
679
|
+
try {
|
|
680
|
+
const payload = await request.json();
|
|
681
|
+
const data = await service.updateContestConfig(payload);
|
|
682
|
+
return NextResponse.json({ success: true, data });
|
|
683
|
+
} catch (error) {
|
|
684
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 400 });
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
};
|
|
688
|
+
var createCreateSubmissionHandler = (config) => {
|
|
689
|
+
const service = resolveService(config);
|
|
690
|
+
return async (request) => {
|
|
691
|
+
try {
|
|
692
|
+
const body = await request.json();
|
|
693
|
+
const mode = body.mode || "web";
|
|
694
|
+
const payload = body.payload;
|
|
695
|
+
if (!payload) {
|
|
696
|
+
return NextResponse.json({ success: false, error: "payload \u4E0D\u80FD\u4E3A\u7A7A" }, { status: 400 });
|
|
697
|
+
}
|
|
698
|
+
const data = await service.createSubmission(payload, mode);
|
|
699
|
+
return NextResponse.json({ success: true, data });
|
|
700
|
+
} catch (error) {
|
|
701
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 400 });
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
};
|
|
705
|
+
var createVoteHandler = (config) => {
|
|
706
|
+
const service = resolveService(config);
|
|
707
|
+
return async (request) => {
|
|
708
|
+
try {
|
|
709
|
+
const payload = await request.json();
|
|
710
|
+
const data = await service.vote(payload);
|
|
711
|
+
return NextResponse.json({ success: true, data });
|
|
712
|
+
} catch (error) {
|
|
713
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 400 });
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
};
|
|
717
|
+
var createReviewSubmissionHandler = (config) => {
|
|
718
|
+
const service = resolveService(config);
|
|
719
|
+
return async (request) => {
|
|
720
|
+
try {
|
|
721
|
+
const payload = await request.json();
|
|
722
|
+
const data = await service.reviewSubmission(payload);
|
|
723
|
+
return NextResponse.json({ success: true, data });
|
|
724
|
+
} catch (error) {
|
|
725
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 400 });
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
};
|
|
729
|
+
var buildSubmissionFilterFromQuery = (request) => {
|
|
730
|
+
const search = request.nextUrl.searchParams;
|
|
731
|
+
const status = search.get("status");
|
|
732
|
+
const type = search.get("type");
|
|
733
|
+
return {
|
|
734
|
+
status: status ? status : void 0,
|
|
735
|
+
type: type ? type : void 0,
|
|
736
|
+
authorId: search.get("authorId") || void 0,
|
|
737
|
+
authorKeyword: search.get("authorKeyword") || void 0,
|
|
738
|
+
titleKeyword: search.get("titleKeyword") || void 0
|
|
739
|
+
};
|
|
740
|
+
};
|
|
741
|
+
var createListSubmissionsHandler = (config) => {
|
|
742
|
+
const service = resolveService(config);
|
|
743
|
+
return async (request) => {
|
|
744
|
+
const filter = buildSubmissionFilterFromQuery(request);
|
|
745
|
+
const data = await service.listSubmissions(filter);
|
|
746
|
+
return NextResponse.json({ success: true, data });
|
|
747
|
+
};
|
|
748
|
+
};
|
|
749
|
+
var createSetVoterRestrictionHandler = (config) => {
|
|
750
|
+
const service = resolveService(config);
|
|
751
|
+
return async (request) => {
|
|
752
|
+
try {
|
|
753
|
+
const payload = await request.json();
|
|
754
|
+
const data = await service.setVoterRestriction(payload);
|
|
755
|
+
return NextResponse.json({ success: true, data });
|
|
756
|
+
} catch (error) {
|
|
757
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 400 });
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
};
|
|
761
|
+
var createResetVotesHandler = (config) => {
|
|
762
|
+
const service = resolveService(config);
|
|
763
|
+
return async (request) => {
|
|
764
|
+
try {
|
|
765
|
+
const payload = await request.json();
|
|
766
|
+
const data = await service.resetVotes(payload);
|
|
767
|
+
return NextResponse.json({ success: true, data });
|
|
768
|
+
} catch (error) {
|
|
769
|
+
return NextResponse.json({ success: false, error: error.message }, { status: 400 });
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
};
|
|
773
|
+
var createExportSubmissionsHandler = (config) => {
|
|
774
|
+
const service = resolveService(config);
|
|
775
|
+
return async (request) => {
|
|
776
|
+
const filter = buildSubmissionFilterFromQuery(request);
|
|
777
|
+
const data = await service.exportSubmissionExcel(filter);
|
|
778
|
+
const body = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
779
|
+
return new NextResponse(body, {
|
|
780
|
+
status: 200,
|
|
781
|
+
headers: {
|
|
782
|
+
"Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
783
|
+
"Content-Disposition": 'attachment; filename="miku-submissions.xlsx"'
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
};
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
export { createCreateSubmissionHandler, createExportSubmissionsHandler, createGetContestSnapshotHandler, createListSubmissionsHandler, createResetVotesHandler, createReviewSubmissionHandler, createSetVoterRestrictionHandler, createUpdateContestConfigHandler, createVoteHandler };
|
|
790
|
+
//# sourceMappingURL=index.mjs.map
|
|
791
|
+
//# sourceMappingURL=index.mjs.map
|