sms-verification-api 0.9.1 → 0.9.7
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/CHANGELOG.md +71 -0
- package/examples/{client.js → client.ts} +105 -119
- package/examples/{libphonenumber-example.js → libphonenumber-example.ts} +1 -1
- package/package.json +16 -14
- package/src/identity-verification-server.ts +385 -261
- package/src/{index.js → index.ts} +1 -1
- package/src/sns.ts +265 -0
- package/src/{verify-phone-server.js → verify-phone-server.ts} +206 -151
- package/src/verify-phone.ts +48 -22
- package/test/{api.test.js → api.test.ts} +20 -16
- package/test/{integration.test.js → integration.test.ts} +10 -10
- package/test/{server.test.js → server.test.ts} +3 -3
- package/test/{verify.test.js → verify.test.ts} +20 -23
- package/test/{voip.test.js → voip.test.ts} +13 -12
- package/tsconfig.json +24 -0
- package/{vitest.config.js → vitest.config.ts} +1 -1
- package/wrangler.toml +1 -4
- package/src/sns.js +0 -236
- /package/test/{metadata-test.js → metadata-test.ts} +0 -0
- /package/test/{setup.js → setup.ts} +0 -0
- /package/test/{utils.test.js → utils.test.ts} +0 -0
|
@@ -10,56 +10,77 @@
|
|
|
10
10
|
* @module verify-phone-server
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { Hono } from "hono";
|
|
14
13
|
import { cors } from "hono/cors";
|
|
15
14
|
import { logger } from "hono/logger";
|
|
16
15
|
import { secureHeaders } from "hono/secure-headers";
|
|
17
16
|
import { rateLimiter } from "hono-rate-limiter";
|
|
18
17
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
19
18
|
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
|
20
|
-
import verifyPhone from "./verify-phone
|
|
19
|
+
import verifyPhone from "./verify-phone";
|
|
20
|
+
|
|
21
|
+
interface Env {
|
|
22
|
+
Bindings: {
|
|
23
|
+
API_KEY?: string;
|
|
24
|
+
AWS_ACCESS_KEY_ID?: string;
|
|
25
|
+
AWS_SECRET_ACCESS_KEY?: string;
|
|
26
|
+
AWS_REGION?: string;
|
|
27
|
+
SMS_SENDER_ID?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
21
30
|
|
|
22
31
|
// Create the main app
|
|
23
|
-
const app = new OpenAPIHono();
|
|
32
|
+
const app = new OpenAPIHono<Env>();
|
|
24
33
|
|
|
25
34
|
// Middleware
|
|
26
35
|
app.use("*", logger());
|
|
27
36
|
app.use("*", secureHeaders());
|
|
28
|
-
app.use(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
app.use(
|
|
38
|
+
"*",
|
|
39
|
+
cors({
|
|
40
|
+
origin: ["*"],
|
|
41
|
+
allowMethods: ["GET", "POST", "OPTIONS"],
|
|
42
|
+
allowHeaders: ["Content-Type", "Authorization", "X-API-Key"],
|
|
43
|
+
maxAge: 86400,
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
34
46
|
|
|
35
47
|
// Rate limiting - lazy loaded to avoid global scope issues
|
|
36
|
-
const createRateLimiter = () =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
const createRateLimiter = () =>
|
|
49
|
+
rateLimiter({
|
|
50
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
51
|
+
limit: 100,
|
|
52
|
+
message: "Too many requests from this IP, please try again later.",
|
|
53
|
+
standardHeaders: true,
|
|
54
|
+
keyGenerator: (c) =>
|
|
55
|
+
c.req.header("CF-Connecting-IP") ||
|
|
56
|
+
c.req.header("X-Forwarded-For") ||
|
|
57
|
+
"unknown",
|
|
58
|
+
} as any);
|
|
43
59
|
|
|
44
60
|
// Apply rate limiting only when the middleware is actually used
|
|
45
61
|
app.use("*", async (c, next) => {
|
|
46
62
|
const limiter = createRateLimiter();
|
|
47
|
-
return limiter(c, next);
|
|
63
|
+
return limiter(c as any, next);
|
|
48
64
|
});
|
|
49
65
|
|
|
50
66
|
// API Key authentication middleware
|
|
51
|
-
const authenticateApiKey = async (c, next) => {
|
|
52
|
-
const apiKey =
|
|
67
|
+
const authenticateApiKey = async (c: any, next: () => Promise<void>) => {
|
|
68
|
+
const apiKey =
|
|
69
|
+
c.req.header("X-API-Key") ||
|
|
70
|
+
c.req.header("Authorization")?.replace("Bearer ", "");
|
|
53
71
|
const expectedApiKey = c.env?.API_KEY;
|
|
54
|
-
|
|
72
|
+
|
|
55
73
|
if (!apiKey || apiKey !== expectedApiKey) {
|
|
56
|
-
return c.json(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
return c.json(
|
|
75
|
+
{
|
|
76
|
+
success: false,
|
|
77
|
+
error: "Unauthorized",
|
|
78
|
+
message: "Invalid or missing API key",
|
|
79
|
+
},
|
|
80
|
+
401,
|
|
81
|
+
);
|
|
61
82
|
}
|
|
62
|
-
|
|
83
|
+
|
|
63
84
|
await next();
|
|
64
85
|
};
|
|
65
86
|
|
|
@@ -73,8 +94,8 @@ app.get("/", (c) => {
|
|
|
73
94
|
health: "/health",
|
|
74
95
|
send: "/api/send",
|
|
75
96
|
verify: "/api/verify",
|
|
76
|
-
docs: "/docs"
|
|
77
|
-
}
|
|
97
|
+
docs: "/docs",
|
|
98
|
+
},
|
|
78
99
|
});
|
|
79
100
|
});
|
|
80
101
|
|
|
@@ -84,7 +105,7 @@ app.get("/health", (c) => {
|
|
|
84
105
|
success: true,
|
|
85
106
|
status: "healthy",
|
|
86
107
|
timestamp: new Date().toISOString(),
|
|
87
|
-
uptime: "N/A" // process.uptime() not available in Cloudflare Workers
|
|
108
|
+
uptime: "N/A", // process.uptime() not available in Cloudflare Workers
|
|
88
109
|
});
|
|
89
110
|
});
|
|
90
111
|
|
|
@@ -113,11 +134,14 @@ const sendRoute = createRoute({
|
|
|
113
134
|
blockVoip: z.boolean().optional().default(false),
|
|
114
135
|
senderId: z.string().optional().default("Verify"),
|
|
115
136
|
messageTemplate: z.string().optional(),
|
|
116
|
-
smsType: z
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
137
|
+
smsType: z
|
|
138
|
+
.enum(["Transactional", "Promotional"])
|
|
139
|
+
.optional()
|
|
140
|
+
.default("Transactional"),
|
|
141
|
+
}),
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
121
145
|
},
|
|
122
146
|
responses: {
|
|
123
147
|
200: {
|
|
@@ -132,11 +156,11 @@ const sendRoute = createRoute({
|
|
|
132
156
|
expiresIn: z.number().optional(),
|
|
133
157
|
error: z.string().optional(),
|
|
134
158
|
details: z.string().optional(),
|
|
135
|
-
isVoip: z.boolean().optional()
|
|
136
|
-
})
|
|
137
|
-
}
|
|
159
|
+
isVoip: z.boolean().optional(),
|
|
160
|
+
}),
|
|
161
|
+
},
|
|
138
162
|
},
|
|
139
|
-
description: "SMS sent successfully"
|
|
163
|
+
description: "SMS sent successfully",
|
|
140
164
|
},
|
|
141
165
|
400: {
|
|
142
166
|
content: {
|
|
@@ -144,11 +168,11 @@ const sendRoute = createRoute({
|
|
|
144
168
|
schema: z.object({
|
|
145
169
|
success: z.boolean(),
|
|
146
170
|
error: z.string(),
|
|
147
|
-
details: z.string().optional()
|
|
148
|
-
})
|
|
149
|
-
}
|
|
171
|
+
details: z.string().optional(),
|
|
172
|
+
}),
|
|
173
|
+
},
|
|
150
174
|
},
|
|
151
|
-
description: "Bad request"
|
|
175
|
+
description: "Bad request",
|
|
152
176
|
},
|
|
153
177
|
401: {
|
|
154
178
|
content: {
|
|
@@ -156,19 +180,20 @@ const sendRoute = createRoute({
|
|
|
156
180
|
schema: z.object({
|
|
157
181
|
success: z.boolean(),
|
|
158
182
|
error: z.string(),
|
|
159
|
-
message: z.string()
|
|
160
|
-
})
|
|
161
|
-
}
|
|
183
|
+
message: z.string(),
|
|
184
|
+
}),
|
|
185
|
+
},
|
|
162
186
|
},
|
|
163
|
-
description: "Unauthorized"
|
|
164
|
-
}
|
|
165
|
-
}
|
|
187
|
+
description: "Unauthorized",
|
|
188
|
+
},
|
|
189
|
+
},
|
|
166
190
|
});
|
|
167
191
|
|
|
168
|
-
app.openapi(sendRoute, async (c) => {
|
|
192
|
+
app.openapi(sendRoute, (async (c: any) => {
|
|
169
193
|
try {
|
|
170
194
|
const body = await c.req.json();
|
|
171
|
-
const { phoneNumber, code, blockVoip, senderId, messageTemplate, smsType } =
|
|
195
|
+
const { phoneNumber, code, blockVoip, senderId, messageTemplate, smsType } =
|
|
196
|
+
body;
|
|
172
197
|
|
|
173
198
|
// Generate code if not provided
|
|
174
199
|
const verificationCode = code || generateCode();
|
|
@@ -177,16 +202,20 @@ app.openapi(sendRoute, async (c) => {
|
|
|
177
202
|
const awsCredentials = {
|
|
178
203
|
accessKeyId: c.env?.AWS_ACCESS_KEY_ID,
|
|
179
204
|
secretAccessKey: c.env?.AWS_SECRET_ACCESS_KEY,
|
|
180
|
-
awsRegion: c.env?.AWS_REGION || "us-east-1"
|
|
205
|
+
awsRegion: c.env?.AWS_REGION || "us-east-1",
|
|
181
206
|
};
|
|
182
207
|
|
|
183
208
|
// Validate AWS credentials
|
|
184
209
|
if (!awsCredentials.accessKeyId || !awsCredentials.secretAccessKey) {
|
|
185
|
-
return c.json(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
210
|
+
return c.json(
|
|
211
|
+
{
|
|
212
|
+
success: false,
|
|
213
|
+
error: "AWS credentials not configured",
|
|
214
|
+
details:
|
|
215
|
+
"Please set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables",
|
|
216
|
+
},
|
|
217
|
+
500,
|
|
218
|
+
);
|
|
190
219
|
}
|
|
191
220
|
|
|
192
221
|
// Send verification SMS
|
|
@@ -197,7 +226,7 @@ app.openapi(sendRoute, async (c) => {
|
|
|
197
226
|
blockVoip,
|
|
198
227
|
senderId: senderId || c.env?.SMS_SENDER_ID || "Verify",
|
|
199
228
|
messageTemplate,
|
|
200
|
-
smsType
|
|
229
|
+
smsType,
|
|
201
230
|
});
|
|
202
231
|
|
|
203
232
|
if (result.success) {
|
|
@@ -207,25 +236,30 @@ app.openapi(sendRoute, async (c) => {
|
|
|
207
236
|
messageId: result.messageId,
|
|
208
237
|
code: result.code,
|
|
209
238
|
phoneNumber: result.phoneNumber,
|
|
210
|
-
expiresIn: result.expiresIn
|
|
239
|
+
expiresIn: result.expiresIn,
|
|
211
240
|
});
|
|
212
241
|
} else {
|
|
213
|
-
return c.json(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
242
|
+
return c.json(
|
|
243
|
+
{
|
|
244
|
+
success: false,
|
|
245
|
+
error: result.error,
|
|
246
|
+
details: result.details,
|
|
247
|
+
isVoip: result.isVoip,
|
|
248
|
+
},
|
|
249
|
+
400,
|
|
250
|
+
);
|
|
219
251
|
}
|
|
220
|
-
|
|
221
252
|
} catch (error) {
|
|
222
|
-
return c.json(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
253
|
+
return c.json(
|
|
254
|
+
{
|
|
255
|
+
success: false,
|
|
256
|
+
error: "Internal server error",
|
|
257
|
+
details: error instanceof Error ? error.message : String(error),
|
|
258
|
+
},
|
|
259
|
+
500,
|
|
260
|
+
);
|
|
227
261
|
}
|
|
228
|
-
});
|
|
262
|
+
}) as any);
|
|
229
263
|
|
|
230
264
|
// Verify SMS code (mock endpoint for demonstration)
|
|
231
265
|
const verifyRoute = createRoute({
|
|
@@ -238,11 +272,11 @@ const verifyRoute = createRoute({
|
|
|
238
272
|
"application/json": {
|
|
239
273
|
schema: z.object({
|
|
240
274
|
phoneNumber: z.string().min(1, "Phone number is required"),
|
|
241
|
-
code: z.string().min(1, "Verification code is required")
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
275
|
+
code: z.string().min(1, "Verification code is required"),
|
|
276
|
+
}),
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
246
280
|
},
|
|
247
281
|
responses: {
|
|
248
282
|
200: {
|
|
@@ -252,50 +286,52 @@ const verifyRoute = createRoute({
|
|
|
252
286
|
success: z.boolean(),
|
|
253
287
|
message: z.string().optional(),
|
|
254
288
|
verified: z.boolean().optional(),
|
|
255
|
-
error: z.string().optional()
|
|
256
|
-
})
|
|
257
|
-
}
|
|
289
|
+
error: z.string().optional(),
|
|
290
|
+
}),
|
|
291
|
+
},
|
|
258
292
|
},
|
|
259
|
-
description: "Code verified successfully"
|
|
293
|
+
description: "Code verified successfully",
|
|
260
294
|
},
|
|
261
295
|
400: {
|
|
262
296
|
content: {
|
|
263
297
|
"application/json": {
|
|
264
298
|
schema: z.object({
|
|
265
299
|
success: z.boolean(),
|
|
266
|
-
error: z.string()
|
|
267
|
-
})
|
|
268
|
-
}
|
|
300
|
+
error: z.string(),
|
|
301
|
+
}),
|
|
302
|
+
},
|
|
269
303
|
},
|
|
270
|
-
description: "Bad request"
|
|
271
|
-
}
|
|
272
|
-
}
|
|
304
|
+
description: "Bad request",
|
|
305
|
+
},
|
|
306
|
+
},
|
|
273
307
|
});
|
|
274
308
|
|
|
275
|
-
app.openapi(verifyRoute, async (c) => {
|
|
309
|
+
app.openapi(verifyRoute, (async (c: any) => {
|
|
276
310
|
try {
|
|
277
311
|
const body = await c.req.json();
|
|
278
312
|
const { phoneNumber, code } = body;
|
|
279
313
|
|
|
280
314
|
// This is a mock verification - in a real app, you'd store codes in a database
|
|
281
315
|
// and verify them against stored values with proper expiration handling
|
|
282
|
-
|
|
316
|
+
|
|
283
317
|
// For demo purposes, we'll just return success
|
|
284
318
|
// In production, implement proper code storage and verification
|
|
285
319
|
return c.json({
|
|
286
320
|
success: true,
|
|
287
321
|
message: "Code verified successfully",
|
|
288
|
-
verified: true
|
|
322
|
+
verified: true,
|
|
289
323
|
});
|
|
290
|
-
|
|
291
324
|
} catch (error) {
|
|
292
|
-
return c.json(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
325
|
+
return c.json(
|
|
326
|
+
{
|
|
327
|
+
success: false,
|
|
328
|
+
error: "Internal server error",
|
|
329
|
+
details: error instanceof Error ? error.message : String(error),
|
|
330
|
+
},
|
|
331
|
+
500,
|
|
332
|
+
);
|
|
297
333
|
}
|
|
298
|
-
});
|
|
334
|
+
}) as any);
|
|
299
335
|
|
|
300
336
|
// General SMS sending endpoint
|
|
301
337
|
const generalSmsRoute = createRoute({
|
|
@@ -310,11 +346,14 @@ const generalSmsRoute = createRoute({
|
|
|
310
346
|
phoneNumber: z.string().min(1, "Phone number is required"),
|
|
311
347
|
message: z.string().min(1, "Message is required"),
|
|
312
348
|
senderId: z.string().optional().default("Verify"),
|
|
313
|
-
smsType: z
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
349
|
+
smsType: z
|
|
350
|
+
.enum(["Transactional", "Promotional"])
|
|
351
|
+
.optional()
|
|
352
|
+
.default("Transactional"),
|
|
353
|
+
}),
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
318
357
|
},
|
|
319
358
|
responses: {
|
|
320
359
|
200: {
|
|
@@ -326,16 +365,16 @@ const generalSmsRoute = createRoute({
|
|
|
326
365
|
messageId: z.string().optional(),
|
|
327
366
|
phoneNumber: z.string().optional(),
|
|
328
367
|
error: z.string().optional(),
|
|
329
|
-
details: z.string().optional()
|
|
330
|
-
})
|
|
331
|
-
}
|
|
368
|
+
details: z.string().optional(),
|
|
369
|
+
}),
|
|
370
|
+
},
|
|
332
371
|
},
|
|
333
|
-
description: "SMS sent successfully"
|
|
334
|
-
}
|
|
335
|
-
}
|
|
372
|
+
description: "SMS sent successfully",
|
|
373
|
+
},
|
|
374
|
+
},
|
|
336
375
|
});
|
|
337
376
|
|
|
338
|
-
app.openapi(generalSmsRoute, async (c) => {
|
|
377
|
+
app.openapi(generalSmsRoute, (async (c: any) => {
|
|
339
378
|
try {
|
|
340
379
|
const body = await c.req.json();
|
|
341
380
|
const { phoneNumber, message, senderId, smsType } = body;
|
|
@@ -344,16 +383,20 @@ app.openapi(generalSmsRoute, async (c) => {
|
|
|
344
383
|
const awsCredentials = {
|
|
345
384
|
accessKeyId: c.env?.AWS_ACCESS_KEY_ID,
|
|
346
385
|
secretAccessKey: c.env?.AWS_SECRET_ACCESS_KEY,
|
|
347
|
-
awsRegion: c.env?.AWS_REGION || "us-east-1"
|
|
386
|
+
awsRegion: c.env?.AWS_REGION || "us-east-1",
|
|
348
387
|
};
|
|
349
388
|
|
|
350
389
|
// Validate AWS credentials
|
|
351
390
|
if (!awsCredentials.accessKeyId || !awsCredentials.secretAccessKey) {
|
|
352
|
-
return c.json(
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
391
|
+
return c.json(
|
|
392
|
+
{
|
|
393
|
+
success: false,
|
|
394
|
+
error: "AWS credentials not configured",
|
|
395
|
+
details:
|
|
396
|
+
"Please set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables",
|
|
397
|
+
},
|
|
398
|
+
500,
|
|
399
|
+
);
|
|
357
400
|
}
|
|
358
401
|
|
|
359
402
|
// Send general SMS
|
|
@@ -364,7 +407,7 @@ app.openapi(generalSmsRoute, async (c) => {
|
|
|
364
407
|
blockVoip: false,
|
|
365
408
|
senderId: senderId || c.env?.SMS_SENDER_ID || "Verify",
|
|
366
409
|
messageTemplate: message,
|
|
367
|
-
smsType
|
|
410
|
+
smsType,
|
|
368
411
|
});
|
|
369
412
|
|
|
370
413
|
if (result.success) {
|
|
@@ -372,52 +415,54 @@ app.openapi(generalSmsRoute, async (c) => {
|
|
|
372
415
|
success: true,
|
|
373
416
|
message: "SMS sent successfully",
|
|
374
417
|
messageId: result.messageId,
|
|
375
|
-
phoneNumber: result.phoneNumber
|
|
418
|
+
phoneNumber: result.phoneNumber,
|
|
376
419
|
});
|
|
377
420
|
} else {
|
|
378
|
-
return c.json(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
421
|
+
return c.json(
|
|
422
|
+
{
|
|
423
|
+
success: false,
|
|
424
|
+
error: result.error,
|
|
425
|
+
details: result.details,
|
|
426
|
+
},
|
|
427
|
+
400,
|
|
428
|
+
);
|
|
383
429
|
}
|
|
384
|
-
|
|
385
430
|
} catch (error) {
|
|
386
|
-
return c.json(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
431
|
+
return c.json(
|
|
432
|
+
{
|
|
433
|
+
success: false,
|
|
434
|
+
error: "Internal server error",
|
|
435
|
+
details: error instanceof Error ? error.message : String(error),
|
|
436
|
+
},
|
|
437
|
+
500,
|
|
438
|
+
);
|
|
391
439
|
}
|
|
392
|
-
});
|
|
440
|
+
}) as any);
|
|
393
441
|
|
|
394
442
|
// OpenAPI documentation
|
|
443
|
+
app.openAPIRegistry.registerComponent("securitySchemes", "apiKey", {
|
|
444
|
+
type: "apiKey",
|
|
445
|
+
name: "X-API-Key",
|
|
446
|
+
in: "header",
|
|
447
|
+
});
|
|
448
|
+
|
|
395
449
|
app.doc("/docs", {
|
|
396
450
|
openapi: "3.0.0",
|
|
397
451
|
info: {
|
|
398
452
|
title: "SMS Verification API",
|
|
399
453
|
version: "1.0.0",
|
|
400
|
-
description: "API for sending SMS verification codes using AWS SNS"
|
|
454
|
+
description: "API for sending SMS verification codes using AWS SNS",
|
|
401
455
|
},
|
|
402
456
|
servers: [
|
|
403
457
|
{
|
|
404
458
|
url: "https://sms-verification-api.your-subdomain.workers.dev",
|
|
405
|
-
description: "Production server"
|
|
459
|
+
description: "Production server",
|
|
406
460
|
},
|
|
407
461
|
{
|
|
408
462
|
url: "http://localhost:8787",
|
|
409
|
-
description: "Development server"
|
|
410
|
-
}
|
|
463
|
+
description: "Development server",
|
|
464
|
+
},
|
|
411
465
|
],
|
|
412
|
-
components: {
|
|
413
|
-
securitySchemes: {
|
|
414
|
-
apiKey: {
|
|
415
|
-
type: "apiKey",
|
|
416
|
-
name: "X-API-Key",
|
|
417
|
-
in: "header"
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
466
|
});
|
|
422
467
|
|
|
423
468
|
// Swagger UI
|
|
@@ -429,20 +474,30 @@ app.use("/api/*", authenticateApiKey);
|
|
|
429
474
|
// Error handling
|
|
430
475
|
app.onError((err, c) => {
|
|
431
476
|
console.error("Server error:", err);
|
|
432
|
-
return c.json(
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
477
|
+
return c.json(
|
|
478
|
+
{
|
|
479
|
+
success: false,
|
|
480
|
+
error: "Internal server error",
|
|
481
|
+
details: err.message,
|
|
482
|
+
},
|
|
483
|
+
500,
|
|
484
|
+
);
|
|
437
485
|
});
|
|
438
486
|
|
|
439
487
|
// 404 handler
|
|
440
488
|
app.notFound((c) => {
|
|
441
|
-
return c.json(
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
489
|
+
return c.json(
|
|
490
|
+
{
|
|
491
|
+
success: false,
|
|
492
|
+
error: "Not found",
|
|
493
|
+
message: "The requested endpoint does not exist",
|
|
494
|
+
},
|
|
495
|
+
404,
|
|
496
|
+
);
|
|
446
497
|
});
|
|
447
498
|
|
|
499
|
+
export { isPhoneNumberVoip } from "./verify-phone";
|
|
500
|
+
|
|
501
|
+
export const createApp = (_env?: Env["Bindings"]) => app;
|
|
502
|
+
|
|
448
503
|
export default app;
|