sa2kit 2.0.2 → 2.0.3
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/README.md +1 -1
- package/dist/calendar/routes/index.d.mts +118 -0
- package/dist/calendar/routes/index.d.ts +118 -0
- package/dist/calendar/routes/index.js +755 -0
- package/dist/calendar/routes/index.js.map +1 -0
- package/dist/calendar/routes/index.mjs +747 -0
- package/dist/calendar/routes/index.mjs.map +1 -0
- package/dist/festivalCard/index.d.mts +3 -2
- package/dist/festivalCard/index.d.ts +3 -2
- package/dist/festivalCard/routes/index.d.mts +42 -0
- package/dist/festivalCard/routes/index.d.ts +42 -0
- package/dist/festivalCard/routes/index.js +361 -0
- package/dist/festivalCard/routes/index.js.map +1 -0
- package/dist/festivalCard/routes/index.mjs +356 -0
- package/dist/festivalCard/routes/index.mjs.map +1 -0
- package/dist/festivalCard/server/index.d.mts +2 -2
- package/dist/festivalCard/server/index.d.ts +2 -2
- package/dist/festivalCardService-BFCRhJrq.d.ts +13 -0
- package/dist/festivalCardService-GriR2VMc.d.mts +13 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/{festivalCardService-CZomuQ4E.d.mts → types-tQfupO6d.d.mts} +1 -11
- package/dist/{festivalCardService-CZomuQ4E.d.ts → types-tQfupO6d.d.ts} +1 -11
- package/package.json +11 -1
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { sql, relations, eq, gte, lte, and, asc, desc } from 'drizzle-orm';
|
|
3
|
+
import { pgEnum, pgTable, boolean, timestamp, jsonb, text, uniqueIndex, foreignKey, integer, varchar, serial, json } from 'drizzle-orm/pg-core';
|
|
4
|
+
|
|
5
|
+
// src/calendar/routes/index.ts
|
|
6
|
+
var userRole = pgEnum("UserRole", ["USER", "ADMIN", "SUPER_ADMIN"]);
|
|
7
|
+
var user = pgTable(
|
|
8
|
+
"User",
|
|
9
|
+
{
|
|
10
|
+
id: text().primaryKey().notNull(),
|
|
11
|
+
email: text().notNull(),
|
|
12
|
+
emailVerified: boolean().default(false).notNull(),
|
|
13
|
+
username: text().notNull(),
|
|
14
|
+
password: text(),
|
|
15
|
+
name: text(),
|
|
16
|
+
nickname: text(),
|
|
17
|
+
image: text(),
|
|
18
|
+
avatar: text(),
|
|
19
|
+
role: userRole().default("USER").notNull(),
|
|
20
|
+
preferences: jsonb(),
|
|
21
|
+
createdAt: timestamp({ precision: 3, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
|
22
|
+
updatedAt: timestamp({ precision: 3, mode: "string" }).notNull(),
|
|
23
|
+
twoFactorEnabled: boolean().default(false).notNull()
|
|
24
|
+
},
|
|
25
|
+
(table) => [
|
|
26
|
+
uniqueIndex("User_email_key").using("btree", table.email.asc().nullsLast().op("text_ops")),
|
|
27
|
+
uniqueIndex("User_username_key").using(
|
|
28
|
+
"btree",
|
|
29
|
+
table.username.asc().nullsLast().op("text_ops")
|
|
30
|
+
)
|
|
31
|
+
]
|
|
32
|
+
);
|
|
33
|
+
var session = pgTable(
|
|
34
|
+
"Session",
|
|
35
|
+
{
|
|
36
|
+
id: text().primaryKey().notNull(),
|
|
37
|
+
userId: text().notNull(),
|
|
38
|
+
token: text().notNull(),
|
|
39
|
+
expiresAt: timestamp({ precision: 3, mode: "string" }).notNull(),
|
|
40
|
+
ipAddress: text(),
|
|
41
|
+
userAgent: text(),
|
|
42
|
+
createdAt: timestamp({ precision: 3, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
|
43
|
+
updatedAt: timestamp({ precision: 3, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull()
|
|
44
|
+
},
|
|
45
|
+
(table) => [
|
|
46
|
+
uniqueIndex("Session_token_key").using("btree", table.token.asc().nullsLast().op("text_ops")),
|
|
47
|
+
foreignKey({
|
|
48
|
+
columns: [table.userId],
|
|
49
|
+
foreignColumns: [user.id],
|
|
50
|
+
name: "Session_userId_fkey"
|
|
51
|
+
}).onUpdate("cascade").onDelete("cascade")
|
|
52
|
+
]
|
|
53
|
+
);
|
|
54
|
+
var account = pgTable(
|
|
55
|
+
"Account",
|
|
56
|
+
{
|
|
57
|
+
id: text().primaryKey().notNull(),
|
|
58
|
+
accountId: text().notNull(),
|
|
59
|
+
providerId: text().notNull(),
|
|
60
|
+
// 提供商: github, google, wechat 等
|
|
61
|
+
userId: text().notNull(),
|
|
62
|
+
accessToken: text(),
|
|
63
|
+
refreshToken: text(),
|
|
64
|
+
idToken: text(),
|
|
65
|
+
accessTokenExpiresAt: timestamp({ precision: 3, mode: "string" }),
|
|
66
|
+
refreshTokenExpiresAt: timestamp({ precision: 3, mode: "string" }),
|
|
67
|
+
scope: text(),
|
|
68
|
+
password: text(),
|
|
69
|
+
createdAt: timestamp({ precision: 3, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
|
70
|
+
updatedAt: timestamp({ precision: 3, mode: "string" }).notNull()
|
|
71
|
+
},
|
|
72
|
+
(table) => [
|
|
73
|
+
uniqueIndex("Account_providerId_accountId_key").using(
|
|
74
|
+
"btree",
|
|
75
|
+
table.providerId.asc().nullsLast().op("text_ops"),
|
|
76
|
+
table.accountId.asc().nullsLast().op("text_ops")
|
|
77
|
+
),
|
|
78
|
+
foreignKey({
|
|
79
|
+
columns: [table.userId],
|
|
80
|
+
foreignColumns: [user.id],
|
|
81
|
+
name: "Account_userId_fkey"
|
|
82
|
+
}).onUpdate("cascade").onDelete("cascade")
|
|
83
|
+
]
|
|
84
|
+
);
|
|
85
|
+
pgTable(
|
|
86
|
+
"verifications",
|
|
87
|
+
{
|
|
88
|
+
id: text().primaryKey().notNull(),
|
|
89
|
+
identifier: text().notNull(),
|
|
90
|
+
// 邮箱或手机号
|
|
91
|
+
value: text().notNull(),
|
|
92
|
+
// 验证码
|
|
93
|
+
expiresAt: timestamp({ precision: 3, mode: "string" }).notNull(),
|
|
94
|
+
createdAt: timestamp({ precision: 3, mode: "string" }).default(sql`CURRENT_TIMESTAMP`).notNull()
|
|
95
|
+
},
|
|
96
|
+
(table) => [
|
|
97
|
+
uniqueIndex("verifications_identifier_value_key").using(
|
|
98
|
+
"btree",
|
|
99
|
+
table.identifier.asc().nullsLast().op("text_ops"),
|
|
100
|
+
table.value.asc().nullsLast().op("text_ops")
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
);
|
|
104
|
+
relations(user, ({ many }) => ({
|
|
105
|
+
sessions: many(session),
|
|
106
|
+
accounts: many(account)
|
|
107
|
+
}));
|
|
108
|
+
relations(session, ({ one }) => ({
|
|
109
|
+
user: one(user, {
|
|
110
|
+
fields: [session.userId],
|
|
111
|
+
references: [user.id]
|
|
112
|
+
})
|
|
113
|
+
}));
|
|
114
|
+
relations(account, ({ one }) => ({
|
|
115
|
+
user: one(user, {
|
|
116
|
+
fields: [account.userId],
|
|
117
|
+
references: [user.id]
|
|
118
|
+
})
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
// src/calendar/db/schema.ts
|
|
122
|
+
var calendarEvents = pgTable("calendar_events", {
|
|
123
|
+
id: serial("id").primaryKey(),
|
|
124
|
+
title: varchar("title", { length: 255 }).notNull(),
|
|
125
|
+
description: text("description"),
|
|
126
|
+
startTime: timestamp("start_time").notNull(),
|
|
127
|
+
endTime: timestamp("end_time").notNull(),
|
|
128
|
+
allDay: boolean("all_day").notNull().default(false),
|
|
129
|
+
location: varchar("location", { length: 500 }),
|
|
130
|
+
color: varchar("color", { length: 7 }).notNull().default("#3B82F6"),
|
|
131
|
+
// 十六进制颜色值
|
|
132
|
+
priority: varchar("priority", { length: 10 }).notNull().default("normal"),
|
|
133
|
+
// low, normal, high, urgent
|
|
134
|
+
userId: integer("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
135
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
136
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
137
|
+
});
|
|
138
|
+
var recurrenceRules = pgTable("recurrence_rules", {
|
|
139
|
+
id: serial("id").primaryKey(),
|
|
140
|
+
eventId: integer("event_id").notNull().references(() => calendarEvents.id, { onDelete: "cascade" }),
|
|
141
|
+
ruleType: varchar("rule_type", { length: 20 }).notNull(),
|
|
142
|
+
// daily, weekly, monthly, yearly, custom
|
|
143
|
+
interval: integer("interval").notNull().default(1),
|
|
144
|
+
// 间隔
|
|
145
|
+
endDate: timestamp("end_date"),
|
|
146
|
+
// 结束日期
|
|
147
|
+
count: integer("count"),
|
|
148
|
+
// 重复次数
|
|
149
|
+
byWeekday: json("by_weekday").$type(),
|
|
150
|
+
// 周几重复 [0,1,2,3,4,5,6],0=周日
|
|
151
|
+
byMonthday: json("by_monthday").$type(),
|
|
152
|
+
// 月中的第几天 [1,2,...,31]
|
|
153
|
+
byMonth: json("by_month").$type(),
|
|
154
|
+
// 第几月 [1,2,...,12]
|
|
155
|
+
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
156
|
+
});
|
|
157
|
+
var reminders = pgTable("reminders", {
|
|
158
|
+
id: serial("id").primaryKey(),
|
|
159
|
+
eventId: integer("event_id").notNull().references(() => calendarEvents.id, { onDelete: "cascade" }),
|
|
160
|
+
reminderTime: timestamp("reminder_time").notNull(),
|
|
161
|
+
// 提醒时间
|
|
162
|
+
reminderType: varchar("reminder_type", { length: 20 }).notNull(),
|
|
163
|
+
// notification, email, sms
|
|
164
|
+
status: varchar("status", { length: 20 }).notNull().default("pending"),
|
|
165
|
+
// pending, sent, failed
|
|
166
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
167
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
168
|
+
});
|
|
169
|
+
var calendarConfigs = pgTable("calendar_configs", {
|
|
170
|
+
id: serial("id").primaryKey(),
|
|
171
|
+
userId: integer("user_id").notNull().references(() => user.id, { onDelete: "cascade" }).unique(),
|
|
172
|
+
firstDayOfWeek: integer("first_day_of_week").notNull().default(1),
|
|
173
|
+
// 0=周日, 1=周一
|
|
174
|
+
workingHoursStart: varchar("working_hours_start", { length: 5 }).notNull().default("09:00"),
|
|
175
|
+
workingHoursEnd: varchar("working_hours_end", { length: 5 }).notNull().default("18:00"),
|
|
176
|
+
timeZone: varchar("time_zone", { length: 50 }).notNull().default("Asia/Shanghai"),
|
|
177
|
+
dateFormat: varchar("date_format", { length: 20 }).notNull().default("YYYY-MM-DD"),
|
|
178
|
+
timeFormat: varchar("time_format", { length: 20 }).notNull().default("HH:mm"),
|
|
179
|
+
defaultView: varchar("default_view", { length: 20 }).notNull().default("month"),
|
|
180
|
+
// month, week, day, agenda
|
|
181
|
+
defaultEventColor: varchar("default_event_color", { length: 7 }).notNull().default("#3B82F6"),
|
|
182
|
+
weekends: boolean("weekends").notNull().default(true),
|
|
183
|
+
eventColors: json("event_colors").$type().default({
|
|
184
|
+
blue: "#3B82F6",
|
|
185
|
+
green: "#10B981",
|
|
186
|
+
purple: "#8B5CF6",
|
|
187
|
+
red: "#EF4444",
|
|
188
|
+
yellow: "#F59E0B",
|
|
189
|
+
pink: "#EC4899",
|
|
190
|
+
indigo: "#6366F1",
|
|
191
|
+
gray: "#6B7280"
|
|
192
|
+
}),
|
|
193
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
194
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
195
|
+
});
|
|
196
|
+
var eventShares = pgTable("event_shares", {
|
|
197
|
+
id: serial("id").primaryKey(),
|
|
198
|
+
eventId: integer("event_id").notNull().references(() => calendarEvents.id, { onDelete: "cascade" }),
|
|
199
|
+
sharedWithUserId: integer("shared_with_user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
200
|
+
sharedByUserId: integer("shared_by_user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
201
|
+
permission: varchar("permission", { length: 20 }).notNull().default("read"),
|
|
202
|
+
// read, write
|
|
203
|
+
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
204
|
+
});
|
|
205
|
+
relations(calendarEvents, ({ one, many }) => ({
|
|
206
|
+
user: one(user, {
|
|
207
|
+
fields: [calendarEvents.userId],
|
|
208
|
+
references: [user.id]
|
|
209
|
+
}),
|
|
210
|
+
recurrenceRule: one(recurrenceRules, {
|
|
211
|
+
fields: [calendarEvents.id],
|
|
212
|
+
references: [recurrenceRules.eventId]
|
|
213
|
+
}),
|
|
214
|
+
reminders: many(reminders),
|
|
215
|
+
shares: many(eventShares)
|
|
216
|
+
}));
|
|
217
|
+
relations(recurrenceRules, ({ one }) => ({
|
|
218
|
+
event: one(calendarEvents, {
|
|
219
|
+
fields: [recurrenceRules.eventId],
|
|
220
|
+
references: [calendarEvents.id]
|
|
221
|
+
})
|
|
222
|
+
}));
|
|
223
|
+
relations(reminders, ({ one }) => ({
|
|
224
|
+
event: one(calendarEvents, {
|
|
225
|
+
fields: [reminders.eventId],
|
|
226
|
+
references: [calendarEvents.id]
|
|
227
|
+
})
|
|
228
|
+
}));
|
|
229
|
+
relations(calendarConfigs, ({ one }) => ({
|
|
230
|
+
user: one(user, {
|
|
231
|
+
fields: [calendarConfigs.userId],
|
|
232
|
+
references: [user.id]
|
|
233
|
+
})
|
|
234
|
+
}));
|
|
235
|
+
relations(eventShares, ({ one }) => ({
|
|
236
|
+
event: one(calendarEvents, {
|
|
237
|
+
fields: [eventShares.eventId],
|
|
238
|
+
references: [calendarEvents.id]
|
|
239
|
+
}),
|
|
240
|
+
sharedWithUser: one(user, {
|
|
241
|
+
fields: [eventShares.sharedWithUserId],
|
|
242
|
+
references: [user.id]
|
|
243
|
+
}),
|
|
244
|
+
sharedByUser: one(user, {
|
|
245
|
+
fields: [eventShares.sharedByUserId],
|
|
246
|
+
references: [user.id]
|
|
247
|
+
})
|
|
248
|
+
}));
|
|
249
|
+
|
|
250
|
+
// src/calendar/db/calendarDbService.ts
|
|
251
|
+
var CalendarDbService = class {
|
|
252
|
+
/**
|
|
253
|
+
* 设置数据库实例
|
|
254
|
+
*/
|
|
255
|
+
setDb(db) {
|
|
256
|
+
this._db = db;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* 数据库是否已初始化
|
|
260
|
+
*/
|
|
261
|
+
isConfigured() {
|
|
262
|
+
return Boolean(this._db);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* 获取数据库实例
|
|
266
|
+
*/
|
|
267
|
+
get db() {
|
|
268
|
+
if (!this._db) {
|
|
269
|
+
throw new Error("CalendarDbService: Database instance not set. Call setDb() first.");
|
|
270
|
+
}
|
|
271
|
+
return this._db;
|
|
272
|
+
}
|
|
273
|
+
// ===== 事件基础操作 =====
|
|
274
|
+
/**
|
|
275
|
+
* 获取用户的所有事件(基础版本)
|
|
276
|
+
*/
|
|
277
|
+
async getAllEvents(userId, startDate, endDate) {
|
|
278
|
+
const conditions = [eq(calendarEvents.userId, userId)];
|
|
279
|
+
if (startDate) {
|
|
280
|
+
conditions.push(gte(calendarEvents.startTime, startDate));
|
|
281
|
+
}
|
|
282
|
+
if (endDate) {
|
|
283
|
+
conditions.push(lte(calendarEvents.endTime, endDate));
|
|
284
|
+
}
|
|
285
|
+
const events = await this.db.select().from(calendarEvents).where(and(...conditions)).orderBy(asc(calendarEvents.startTime));
|
|
286
|
+
return events;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* 根据ID获取事件
|
|
290
|
+
*/
|
|
291
|
+
async getEventById(eventId) {
|
|
292
|
+
const [event] = await this.db.select().from(calendarEvents).where(eq(calendarEvents.id, eventId)).limit(1);
|
|
293
|
+
return event || null;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* 创建事件(基础版本)
|
|
297
|
+
*/
|
|
298
|
+
async createEvent(eventData) {
|
|
299
|
+
const [newEvent] = await this.db.insert(calendarEvents).values(eventData).returning();
|
|
300
|
+
return newEvent;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* 更新事件
|
|
304
|
+
*/
|
|
305
|
+
async updateEvent(eventId, eventData) {
|
|
306
|
+
const updateData = {
|
|
307
|
+
...eventData,
|
|
308
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
309
|
+
};
|
|
310
|
+
const [updatedEvent] = await this.db.update(calendarEvents).set(updateData).where(eq(calendarEvents.id, eventId)).returning();
|
|
311
|
+
return updatedEvent;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* 删除事件
|
|
315
|
+
*/
|
|
316
|
+
async deleteEvent(eventId) {
|
|
317
|
+
await this.db.delete(calendarEvents).where(eq(calendarEvents.id, eventId));
|
|
318
|
+
}
|
|
319
|
+
// ===== 用户配置操作 =====
|
|
320
|
+
/**
|
|
321
|
+
* 获取用户的日历配置
|
|
322
|
+
*/
|
|
323
|
+
async getUserConfig(userId) {
|
|
324
|
+
const [config] = await this.db.select().from(calendarConfigs).where(eq(calendarConfigs.userId, userId)).limit(1);
|
|
325
|
+
return config || null;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 创建或更新用户配置
|
|
329
|
+
*/
|
|
330
|
+
async upsertUserConfig(userId, configData) {
|
|
331
|
+
const existingConfig = await this.getUserConfig(userId);
|
|
332
|
+
if (existingConfig) {
|
|
333
|
+
const [updatedConfig] = await this.db.update(calendarConfigs).set({
|
|
334
|
+
...configData,
|
|
335
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
336
|
+
}).where(eq(calendarConfigs.userId, userId)).returning();
|
|
337
|
+
return updatedConfig;
|
|
338
|
+
} else {
|
|
339
|
+
const [newConfig] = await this.db.insert(calendarConfigs).values({
|
|
340
|
+
userId,
|
|
341
|
+
...configData
|
|
342
|
+
}).returning();
|
|
343
|
+
return newConfig;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* 为事件创建重复规则
|
|
348
|
+
*/
|
|
349
|
+
async createRecurrenceRule(ruleData) {
|
|
350
|
+
const [newRule] = await this.db.insert(recurrenceRules).values(ruleData).returning();
|
|
351
|
+
return newRule;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* 获取事件的重复规则
|
|
355
|
+
*/
|
|
356
|
+
async getRecurrenceRule(eventId) {
|
|
357
|
+
const [rule] = await this.db.select().from(recurrenceRules).where(eq(recurrenceRules.eventId, eventId)).limit(1);
|
|
358
|
+
return rule || null;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* 删除重复规则
|
|
362
|
+
*/
|
|
363
|
+
async deleteRecurrenceRule(eventId) {
|
|
364
|
+
await this.db.delete(recurrenceRules).where(eq(recurrenceRules.eventId, eventId));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* 为事件创建提醒
|
|
368
|
+
*/
|
|
369
|
+
async createReminder(reminderData) {
|
|
370
|
+
const [newReminder] = await this.db.insert(reminders).values({
|
|
371
|
+
...reminderData,
|
|
372
|
+
status: reminderData.status || "pending"
|
|
373
|
+
}).returning();
|
|
374
|
+
return newReminder;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* 获取事件的提醒列表
|
|
378
|
+
*/
|
|
379
|
+
async getEventReminders(eventId) {
|
|
380
|
+
const remindersList = await this.db.select().from(reminders).where(eq(reminders.eventId, eventId)).orderBy(asc(reminders.reminderTime));
|
|
381
|
+
return remindersList;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* 删除事件的所有提醒
|
|
385
|
+
*/
|
|
386
|
+
async deleteEventReminders(eventId) {
|
|
387
|
+
await this.db.delete(reminders).where(eq(reminders.eventId, eventId));
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* 获取用户在指定时间范围内的事件数量
|
|
391
|
+
*/
|
|
392
|
+
async getEventCount(userId, startDate, endDate) {
|
|
393
|
+
const conditions = [eq(calendarEvents.userId, userId)];
|
|
394
|
+
if (startDate) {
|
|
395
|
+
conditions.push(gte(calendarEvents.startTime, startDate));
|
|
396
|
+
}
|
|
397
|
+
if (endDate) {
|
|
398
|
+
conditions.push(lte(calendarEvents.endTime, endDate));
|
|
399
|
+
}
|
|
400
|
+
const result = await this.db.select({ count: calendarEvents.id }).from(calendarEvents).where(and(...conditions));
|
|
401
|
+
return result.length;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* 搜索事件
|
|
405
|
+
*/
|
|
406
|
+
async searchEvents(userId, searchTerm) {
|
|
407
|
+
const events = await this.db.select().from(calendarEvents).where(
|
|
408
|
+
and(
|
|
409
|
+
eq(calendarEvents.userId, userId)
|
|
410
|
+
)
|
|
411
|
+
).orderBy(desc(calendarEvents.startTime));
|
|
412
|
+
return events.filter(
|
|
413
|
+
(event) => event.title.toLowerCase().includes(searchTerm.toLowerCase()) || event.description && event.description.toLowerCase().includes(searchTerm.toLowerCase())
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* 批量删除用户的所有事件
|
|
418
|
+
*/
|
|
419
|
+
async deleteAllUserEvents(userId) {
|
|
420
|
+
await this.db.delete(calendarEvents).where(eq(calendarEvents.userId, userId));
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var calendarDbService = new CalendarDbService();
|
|
424
|
+
|
|
425
|
+
// src/calendar/routes/index.ts
|
|
426
|
+
function initDbService(db) {
|
|
427
|
+
if (db) {
|
|
428
|
+
calendarDbService.setDb(db);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function createGetEventsHandler(config) {
|
|
432
|
+
initDbService(config.db);
|
|
433
|
+
return async (request) => {
|
|
434
|
+
try {
|
|
435
|
+
const user2 = await config.validateAuth(request);
|
|
436
|
+
if (!user2) {
|
|
437
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
438
|
+
}
|
|
439
|
+
const { searchParams } = new URL(request.url);
|
|
440
|
+
const startDateStr = searchParams.get("startDate");
|
|
441
|
+
const endDateStr = searchParams.get("endDate");
|
|
442
|
+
let startDate;
|
|
443
|
+
let endDate;
|
|
444
|
+
if (startDateStr) {
|
|
445
|
+
startDate = new Date(startDateStr);
|
|
446
|
+
if (isNaN(startDate.getTime())) {
|
|
447
|
+
return NextResponse.json({ success: false, error: "\u5F00\u59CB\u65E5\u671F\u683C\u5F0F\u65E0\u6548" }, { status: 400 });
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (endDateStr) {
|
|
451
|
+
endDate = new Date(endDateStr);
|
|
452
|
+
if (isNaN(endDate.getTime())) {
|
|
453
|
+
return NextResponse.json({ success: false, error: "\u7ED3\u675F\u65E5\u671F\u683C\u5F0F\u65E0\u6548" }, { status: 400 });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const events = await calendarDbService.getAllEvents(user2.id, startDate, endDate);
|
|
457
|
+
return NextResponse.json({
|
|
458
|
+
success: true,
|
|
459
|
+
data: events,
|
|
460
|
+
message: "\u83B7\u53D6\u4E8B\u4EF6\u5217\u8868\u6210\u529F"
|
|
461
|
+
});
|
|
462
|
+
} catch (error) {
|
|
463
|
+
console.error("\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25\uFF1A", error);
|
|
464
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function createCreateEventHandler(config) {
|
|
469
|
+
initDbService(config.db);
|
|
470
|
+
return async (request) => {
|
|
471
|
+
try {
|
|
472
|
+
const user2 = await config.validateAuth(request);
|
|
473
|
+
if (!user2) {
|
|
474
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
475
|
+
}
|
|
476
|
+
const body = await request.json();
|
|
477
|
+
if (!body.title || typeof body.title !== "string" || body.title.trim() === "") {
|
|
478
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A" }, { status: 400 });
|
|
479
|
+
}
|
|
480
|
+
if (!body.startTime || !body.endTime) {
|
|
481
|
+
return NextResponse.json({ success: false, error: "\u5F00\u59CB\u65F6\u95F4\u548C\u7ED3\u675F\u65F6\u95F4\u4E0D\u80FD\u4E3A\u7A7A" }, { status: 400 });
|
|
482
|
+
}
|
|
483
|
+
const startTime = new Date(body.startTime);
|
|
484
|
+
const endTime = new Date(body.endTime);
|
|
485
|
+
if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) {
|
|
486
|
+
return NextResponse.json({ success: false, error: "\u65E5\u671F\u683C\u5F0F\u65E0\u6548" }, { status: 400 });
|
|
487
|
+
}
|
|
488
|
+
if (startTime.getTime() >= endTime.getTime()) {
|
|
489
|
+
return NextResponse.json({ success: false, error: "\u7ED3\u675F\u65F6\u95F4\u5FC5\u987B\u665A\u4E8E\u5F00\u59CB\u65F6\u95F4" }, { status: 400 });
|
|
490
|
+
}
|
|
491
|
+
const eventData = {
|
|
492
|
+
title: body.title.trim(),
|
|
493
|
+
description: body.description || null,
|
|
494
|
+
startTime,
|
|
495
|
+
endTime,
|
|
496
|
+
allDay: Boolean(body.allDay),
|
|
497
|
+
location: body.location || null,
|
|
498
|
+
color: body.color || "#3B82F6",
|
|
499
|
+
userId: user2.id
|
|
500
|
+
};
|
|
501
|
+
const newEvent = await calendarDbService.createEvent(eventData);
|
|
502
|
+
if (body.recurrence) {
|
|
503
|
+
await calendarDbService.createRecurrenceRule({
|
|
504
|
+
eventId: newEvent.id,
|
|
505
|
+
...body.recurrence,
|
|
506
|
+
endDate: body.recurrence.endDate ? new Date(body.recurrence.endDate) : void 0
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
if (body.reminders && Array.isArray(body.reminders)) {
|
|
510
|
+
for (const reminder of body.reminders) {
|
|
511
|
+
if (reminder.reminderTime) {
|
|
512
|
+
await calendarDbService.createReminder({
|
|
513
|
+
eventId: newEvent.id,
|
|
514
|
+
reminderTime: new Date(reminder.reminderTime),
|
|
515
|
+
reminderType: reminder.reminderType || "notification",
|
|
516
|
+
status: "pending"
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return NextResponse.json({
|
|
522
|
+
success: true,
|
|
523
|
+
data: newEvent,
|
|
524
|
+
message: "\u521B\u5EFA\u4E8B\u4EF6\u6210\u529F"
|
|
525
|
+
});
|
|
526
|
+
} catch (error) {
|
|
527
|
+
console.error("\u521B\u5EFA\u4E8B\u4EF6\u5931\u8D25\uFF1A", error);
|
|
528
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function createGetEventByIdHandler(config) {
|
|
533
|
+
initDbService(config.db);
|
|
534
|
+
return async (request, { params }) => {
|
|
535
|
+
try {
|
|
536
|
+
const user2 = await config.validateAuth(request);
|
|
537
|
+
if (!user2) {
|
|
538
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
539
|
+
}
|
|
540
|
+
const eventId = parseInt(params.id);
|
|
541
|
+
if (isNaN(eventId)) {
|
|
542
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6ID\u65E0\u6548" }, { status: 400 });
|
|
543
|
+
}
|
|
544
|
+
const event = await calendarDbService.getEventById(eventId);
|
|
545
|
+
if (!event) {
|
|
546
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6\u4E0D\u5B58\u5728" }, { status: 404 });
|
|
547
|
+
}
|
|
548
|
+
if (event.userId !== user2.id) {
|
|
549
|
+
return NextResponse.json({ success: false, error: "\u65E0\u6743\u8BBF\u95EE\u6B64\u4E8B\u4EF6" }, { status: 403 });
|
|
550
|
+
}
|
|
551
|
+
const [recurrenceRule, reminders2] = await Promise.all([
|
|
552
|
+
calendarDbService.getRecurrenceRule(eventId),
|
|
553
|
+
calendarDbService.getEventReminders(eventId)
|
|
554
|
+
]);
|
|
555
|
+
return NextResponse.json({
|
|
556
|
+
success: true,
|
|
557
|
+
data: {
|
|
558
|
+
...event,
|
|
559
|
+
recurrenceRule: recurrenceRule || void 0,
|
|
560
|
+
reminders: reminders2 || []
|
|
561
|
+
},
|
|
562
|
+
message: "\u83B7\u53D6\u4E8B\u4EF6\u6210\u529F"
|
|
563
|
+
});
|
|
564
|
+
} catch (error) {
|
|
565
|
+
console.error("\u83B7\u53D6\u4E8B\u4EF6\u5931\u8D25\uFF1A", error);
|
|
566
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function createUpdateEventHandler(config) {
|
|
571
|
+
initDbService(config.db);
|
|
572
|
+
return async (request, { params }) => {
|
|
573
|
+
try {
|
|
574
|
+
const user2 = await config.validateAuth(request);
|
|
575
|
+
if (!user2) {
|
|
576
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
577
|
+
}
|
|
578
|
+
const eventId = parseInt(params.id);
|
|
579
|
+
if (isNaN(eventId)) {
|
|
580
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6ID\u65E0\u6548" }, { status: 400 });
|
|
581
|
+
}
|
|
582
|
+
const existingEvent = await calendarDbService.getEventById(eventId);
|
|
583
|
+
if (!existingEvent || existingEvent.userId !== user2.id) {
|
|
584
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u4FEE\u6539" }, { status: 404 });
|
|
585
|
+
}
|
|
586
|
+
const body = await request.json();
|
|
587
|
+
const updateData = {};
|
|
588
|
+
if (body.title !== void 0) updateData.title = body.title.trim();
|
|
589
|
+
if (body.description !== void 0) updateData.description = body.description;
|
|
590
|
+
if (body.startTime !== void 0) updateData.startTime = new Date(body.startTime);
|
|
591
|
+
if (body.endTime !== void 0) updateData.endTime = new Date(body.endTime);
|
|
592
|
+
if (body.allDay !== void 0) updateData.allDay = Boolean(body.allDay);
|
|
593
|
+
if (body.location !== void 0) updateData.location = body.location;
|
|
594
|
+
if (body.color !== void 0) updateData.color = body.color;
|
|
595
|
+
const updatedEvent = await calendarDbService.updateEvent(eventId, updateData);
|
|
596
|
+
if (body.recurrence !== void 0) {
|
|
597
|
+
await calendarDbService.deleteRecurrenceRule(eventId);
|
|
598
|
+
if (body.recurrence) {
|
|
599
|
+
await calendarDbService.createRecurrenceRule({
|
|
600
|
+
eventId,
|
|
601
|
+
...body.recurrence,
|
|
602
|
+
endDate: body.recurrence.endDate ? new Date(body.recurrence.endDate) : void 0
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (body.reminders !== void 0) {
|
|
607
|
+
await calendarDbService.deleteEventReminders(eventId);
|
|
608
|
+
if (Array.isArray(body.reminders)) {
|
|
609
|
+
for (const reminder of body.reminders) {
|
|
610
|
+
if (reminder.reminderTime) {
|
|
611
|
+
await calendarDbService.createReminder({
|
|
612
|
+
eventId,
|
|
613
|
+
reminderTime: new Date(reminder.reminderTime),
|
|
614
|
+
reminderType: reminder.reminderType || "notification",
|
|
615
|
+
status: "pending"
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return NextResponse.json({
|
|
622
|
+
success: true,
|
|
623
|
+
data: updatedEvent,
|
|
624
|
+
message: "\u66F4\u65B0\u4E8B\u4EF6\u6210\u529F"
|
|
625
|
+
});
|
|
626
|
+
} catch (error) {
|
|
627
|
+
console.error("\u66F4\u65B0\u4E8B\u4EF6\u5931\u8D25\uFF1A", error);
|
|
628
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function createDeleteEventHandler(config) {
|
|
633
|
+
initDbService(config.db);
|
|
634
|
+
return async (request, { params }) => {
|
|
635
|
+
try {
|
|
636
|
+
const user2 = await config.validateAuth(request);
|
|
637
|
+
if (!user2) {
|
|
638
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
639
|
+
}
|
|
640
|
+
const eventId = parseInt(params.id);
|
|
641
|
+
if (isNaN(eventId)) {
|
|
642
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6ID\u65E0\u6548" }, { status: 400 });
|
|
643
|
+
}
|
|
644
|
+
const existingEvent = await calendarDbService.getEventById(eventId);
|
|
645
|
+
if (!existingEvent || existingEvent.userId !== user2.id) {
|
|
646
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u5220\u9664" }, { status: 404 });
|
|
647
|
+
}
|
|
648
|
+
await calendarDbService.deleteEvent(eventId);
|
|
649
|
+
return NextResponse.json({
|
|
650
|
+
success: true,
|
|
651
|
+
message: "\u5220\u9664\u4E8B\u4EF6\u6210\u529F"
|
|
652
|
+
});
|
|
653
|
+
} catch (error) {
|
|
654
|
+
console.error("\u5220\u9664\u4E8B\u4EF6\u5931\u8D25\uFF1A", error);
|
|
655
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
function createBatchDeleteEventsHandler(config) {
|
|
660
|
+
initDbService(config.db);
|
|
661
|
+
return async (request) => {
|
|
662
|
+
try {
|
|
663
|
+
const user2 = await config.validateAuth(request);
|
|
664
|
+
if (!user2) {
|
|
665
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
666
|
+
}
|
|
667
|
+
const body = await request.json();
|
|
668
|
+
if (!body.eventIds || !Array.isArray(body.eventIds)) {
|
|
669
|
+
return NextResponse.json({ success: false, error: "\u4E8B\u4EF6ID\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A" }, { status: 400 });
|
|
670
|
+
}
|
|
671
|
+
const results = await Promise.all(
|
|
672
|
+
body.eventIds.map(async (id) => {
|
|
673
|
+
const event = await calendarDbService.getEventById(id);
|
|
674
|
+
if (event && event.userId === user2.id) {
|
|
675
|
+
await calendarDbService.deleteEvent(id);
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
return false;
|
|
679
|
+
})
|
|
680
|
+
);
|
|
681
|
+
const deletedCount = results.filter(Boolean).length;
|
|
682
|
+
return NextResponse.json({
|
|
683
|
+
success: true,
|
|
684
|
+
data: { deletedCount },
|
|
685
|
+
message: "\u6210\u529F\u5220\u9664 " + deletedCount + " \u4E2A\u4E8B\u4EF6"
|
|
686
|
+
});
|
|
687
|
+
} catch (error) {
|
|
688
|
+
console.error("\u6279\u91CF\u5220\u9664\u4E8B\u4EF6\u5931\u8D25\uFF1A", error);
|
|
689
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
function createConfigHandler(config) {
|
|
694
|
+
initDbService(config.db);
|
|
695
|
+
return {
|
|
696
|
+
GET: async (request) => {
|
|
697
|
+
try {
|
|
698
|
+
const user2 = await config.validateAuth(request);
|
|
699
|
+
if (!user2) {
|
|
700
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
701
|
+
}
|
|
702
|
+
const dbConfig = await calendarDbService.getUserConfig(user2.id);
|
|
703
|
+
return NextResponse.json({
|
|
704
|
+
success: true,
|
|
705
|
+
data: dbConfig || {
|
|
706
|
+
firstDayOfWeek: 1,
|
|
707
|
+
workingHoursStart: "09:00",
|
|
708
|
+
workingHoursEnd: "18:00",
|
|
709
|
+
timeZone: "Asia/Shanghai",
|
|
710
|
+
dateFormat: "YYYY-MM-DD",
|
|
711
|
+
timeFormat: "HH:mm",
|
|
712
|
+
defaultView: "month",
|
|
713
|
+
defaultEventColor: "#3B82F6",
|
|
714
|
+
weekends: true,
|
|
715
|
+
eventColors: {}
|
|
716
|
+
},
|
|
717
|
+
message: "\u83B7\u53D6\u914D\u7F6E\u6210\u529F"
|
|
718
|
+
});
|
|
719
|
+
} catch (error) {
|
|
720
|
+
console.error("\u83B7\u53D6\u914D\u7F6E\u5931\u8D25\uFF1A", error);
|
|
721
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
PUT: async (request) => {
|
|
725
|
+
try {
|
|
726
|
+
const user2 = await config.validateAuth(request);
|
|
727
|
+
if (!user2) {
|
|
728
|
+
return NextResponse.json({ success: false, error: "\u672A\u6388\u6743\u8BBF\u95EE" }, { status: 401 });
|
|
729
|
+
}
|
|
730
|
+
const body = await request.json();
|
|
731
|
+
const updatedConfig = await calendarDbService.upsertUserConfig(user2.id, body);
|
|
732
|
+
return NextResponse.json({
|
|
733
|
+
success: true,
|
|
734
|
+
data: updatedConfig,
|
|
735
|
+
message: "\u66F4\u65B0\u914D\u7F6E\u6210\u529F"
|
|
736
|
+
});
|
|
737
|
+
} catch (error) {
|
|
738
|
+
console.error("\u66F4\u65B0\u914D\u7F6E\u5931\u8D25\uFF1A", error);
|
|
739
|
+
return NextResponse.json({ success: false, error: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF" }, { status: 500 });
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
export { createBatchDeleteEventsHandler, createConfigHandler, createCreateEventHandler, createDeleteEventHandler, createGetEventByIdHandler, createGetEventsHandler, createUpdateEventHandler };
|
|
746
|
+
//# sourceMappingURL=index.mjs.map
|
|
747
|
+
//# sourceMappingURL=index.mjs.map
|