@bbki.ng/backend 0.3.6 → 0.3.9
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 +19 -0
- package/README.md +1 -1
- package/eslint.config.js +1 -4
- package/migrations/001_add_posts.sql +13 -0
- package/migrations/002_seed_test_data.sql +15 -0
- package/package.json +2 -2
- package/src/config/app.config.ts +6 -6
- package/src/controllers/comment/add.controller.ts +6 -6
- package/src/controllers/posts/add.controller.ts +90 -0
- package/src/controllers/posts/get.controller.ts +42 -0
- package/src/controllers/posts/list.controller.ts +25 -0
- package/src/controllers/posts/remove.controller.ts +47 -0
- package/src/controllers/posts/update.controller.ts +101 -0
- package/src/controllers/streaming/add.controller.ts +32 -27
- package/src/controllers/streaming/list.controller.ts +22 -13
- package/src/controllers/streaming/remove.controller.ts +24 -25
- package/src/index.ts +2 -0
- package/src/routes/comment.routes.ts +3 -3
- package/src/routes/posts.routes.ts +20 -0
- package/src/routes/streaming.routes.ts +7 -7
- package/src/types/index.ts +10 -1
- package/src/utils/auth.ts +40 -0
- package/tsconfig.json +2 -2
- package/worker-configuration.d.ts +10010 -8896
- package/wrangler.jsonc +31 -33
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Context } from
|
|
2
|
-
import { HTTPException } from
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
import { HTTPException } from 'hono/http-exception';
|
|
3
3
|
|
|
4
4
|
const timingSafeEqual = (a: string, b: string): boolean => {
|
|
5
5
|
if (a.length !== b.length) return false;
|
|
@@ -16,58 +16,57 @@ export const removeStreaming = async (c: Context) => {
|
|
|
16
16
|
const streamApiKey = c.env?.STREAM_API_KEY;
|
|
17
17
|
if (!streamApiKey) {
|
|
18
18
|
console.error('STREAM_API_KEY not configured in environment');
|
|
19
|
-
throw new HTTPException(500, { message:
|
|
19
|
+
throw new HTTPException(500, { message: 'Server configuration error' });
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// 1. Authentication (Header method)
|
|
23
23
|
const apiKey = c.req.header('x-api-key');
|
|
24
24
|
if (!apiKey || !timingSafeEqual(apiKey, streamApiKey)) {
|
|
25
|
-
throw new HTTPException(401, { message:
|
|
25
|
+
throw new HTTPException(401, { message: 'Invalid API key' });
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// 2. Get ID from URL params
|
|
29
29
|
const id = c.req.param('id');
|
|
30
30
|
if (!id) {
|
|
31
|
-
throw new HTTPException(400, { message:
|
|
31
|
+
throw new HTTPException(400, { message: 'ID is required' });
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// 3. Database check
|
|
35
35
|
if (!c.env?.DB) {
|
|
36
|
-
throw new HTTPException(503, { message:
|
|
36
|
+
throw new HTTPException(503, { message: 'Database unavailable' });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// 4. Check if stream exists
|
|
40
|
-
const existing = await c.env.DB.prepare(
|
|
41
|
-
"SELECT id FROM streaming WHERE id = ?"
|
|
42
|
-
)
|
|
40
|
+
const existing = await c.env.DB.prepare('SELECT id FROM streaming WHERE id = ?')
|
|
43
41
|
.bind(id)
|
|
44
42
|
.first();
|
|
45
43
|
|
|
46
44
|
if (!existing) {
|
|
47
|
-
throw new HTTPException(404, { message:
|
|
45
|
+
throw new HTTPException(404, { message: 'Stream not found' });
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
// 5. Delete the stream
|
|
51
|
-
await c.env.DB.prepare(
|
|
52
|
-
"DELETE FROM streaming WHERE id = ?"
|
|
53
|
-
)
|
|
54
|
-
.bind(id)
|
|
55
|
-
.run();
|
|
56
|
-
|
|
57
|
-
return c.json({
|
|
58
|
-
status: "success",
|
|
59
|
-
message: "Stream deleted successfully"
|
|
60
|
-
}, 200);
|
|
49
|
+
await c.env.DB.prepare('DELETE FROM streaming WHERE id = ?').bind(id).run();
|
|
61
50
|
|
|
51
|
+
return c.json(
|
|
52
|
+
{
|
|
53
|
+
status: 'success',
|
|
54
|
+
message: 'Stream deleted successfully',
|
|
55
|
+
},
|
|
56
|
+
200
|
|
57
|
+
);
|
|
62
58
|
} catch (error) {
|
|
63
59
|
if (error instanceof HTTPException) {
|
|
64
|
-
return c.json({ status:
|
|
60
|
+
return c.json({ status: 'error', message: error.message }, error.status);
|
|
65
61
|
}
|
|
66
62
|
|
|
67
63
|
console.error('Unexpected error:', error);
|
|
68
|
-
return c.json(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
return c.json(
|
|
65
|
+
{
|
|
66
|
+
status: 'error',
|
|
67
|
+
message: 'Internal server error',
|
|
68
|
+
},
|
|
69
|
+
500
|
|
70
|
+
);
|
|
72
71
|
}
|
|
73
72
|
};
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,11 @@ import app from './config/app.config';
|
|
|
2
2
|
|
|
3
3
|
import { commentRouter } from './routes/comment.routes';
|
|
4
4
|
import { streamingRouter } from './routes/streaming.routes';
|
|
5
|
+
import { postsRouter } from './routes/posts.routes';
|
|
5
6
|
|
|
6
7
|
app.route('comment', commentRouter);
|
|
7
8
|
app.route('streaming', streamingRouter);
|
|
9
|
+
app.route('posts', postsRouter);
|
|
8
10
|
|
|
9
11
|
app.get('/', c => {
|
|
10
12
|
return c.text('Hello Hono!');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Hono } from
|
|
2
|
-
import { addComment } from
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { addComment } from '../controllers/comment/add.controller';
|
|
3
3
|
|
|
4
4
|
const commentRouter = new Hono();
|
|
5
5
|
|
|
6
|
-
commentRouter.post(
|
|
6
|
+
commentRouter.post('/add', addComment);
|
|
7
7
|
|
|
8
8
|
export { commentRouter };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { listPosts } from '../controllers/posts/list.controller';
|
|
3
|
+
import { getPost } from '../controllers/posts/get.controller';
|
|
4
|
+
import { addPost } from '../controllers/posts/add.controller';
|
|
5
|
+
import { updatePost } from '../controllers/posts/update.controller';
|
|
6
|
+
import { removePost } from '../controllers/posts/remove.controller';
|
|
7
|
+
import { requireAuth } from '../utils/auth';
|
|
8
|
+
|
|
9
|
+
const postsRouter = new Hono();
|
|
10
|
+
|
|
11
|
+
// Public routes
|
|
12
|
+
postsRouter.get('/', listPosts);
|
|
13
|
+
postsRouter.get('/:title', getPost);
|
|
14
|
+
|
|
15
|
+
// Protected routes (require API key)
|
|
16
|
+
postsRouter.post('/', requireAuth, addPost);
|
|
17
|
+
postsRouter.put('/:id', requireAuth, updatePost);
|
|
18
|
+
postsRouter.delete('/:id', requireAuth, removePost);
|
|
19
|
+
|
|
20
|
+
export { postsRouter };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Hono } from
|
|
2
|
-
import { listStreaming } from
|
|
3
|
-
import { addStreaming } from
|
|
4
|
-
import { removeStreaming } from
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { listStreaming } from '../controllers/streaming/list.controller';
|
|
3
|
+
import { addStreaming } from '../controllers/streaming/add.controller';
|
|
4
|
+
import { removeStreaming } from '../controllers/streaming/remove.controller';
|
|
5
5
|
|
|
6
6
|
const streamingRouter = new Hono();
|
|
7
7
|
|
|
8
|
-
streamingRouter.get(
|
|
9
|
-
streamingRouter.post(
|
|
10
|
-
streamingRouter.delete(
|
|
8
|
+
streamingRouter.get('/', listStreaming);
|
|
9
|
+
streamingRouter.post('/', addStreaming);
|
|
10
|
+
streamingRouter.delete('/:id', removeStreaming);
|
|
11
11
|
|
|
12
12
|
export { streamingRouter };
|
package/src/types/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type Passkey = {
|
|
|
9
9
|
userId: string;
|
|
10
10
|
publicKey: string; // base64url
|
|
11
11
|
counter: number;
|
|
12
|
-
deviceType:
|
|
12
|
+
deviceType: 'singleDevice' | 'multiDevice';
|
|
13
13
|
backedUp: boolean;
|
|
14
14
|
transports?: string[]; // 存为 JSON 字符串
|
|
15
15
|
};
|
|
@@ -21,3 +21,12 @@ export type Streaming = {
|
|
|
21
21
|
type?: string;
|
|
22
22
|
createdAt: string;
|
|
23
23
|
};
|
|
24
|
+
|
|
25
|
+
export type Post = {
|
|
26
|
+
id: string;
|
|
27
|
+
title: string;
|
|
28
|
+
content: string;
|
|
29
|
+
author: string;
|
|
30
|
+
createdAt: string;
|
|
31
|
+
updatedAt: string;
|
|
32
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
import { HTTPException } from 'hono/http-exception';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Timing-safe string comparison to prevent timing attacks
|
|
6
|
+
*/
|
|
7
|
+
export const timingSafeEqual = (a: string, b: string): boolean => {
|
|
8
|
+
if (a.length !== b.length) return false;
|
|
9
|
+
let result = 0;
|
|
10
|
+
for (let i = 0; i < a.length; i++) {
|
|
11
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
12
|
+
}
|
|
13
|
+
return result === 0;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validate API Key from request header
|
|
18
|
+
* Returns true if valid, false otherwise
|
|
19
|
+
*/
|
|
20
|
+
export const validateApiKey = (c: Context): boolean => {
|
|
21
|
+
const apiKey = c.req.header('x-api-key');
|
|
22
|
+
const streamApiKey = c.env?.STREAM_API_KEY;
|
|
23
|
+
|
|
24
|
+
if (!apiKey || !streamApiKey) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return timingSafeEqual(apiKey, streamApiKey);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Middleware to require API Key authentication
|
|
33
|
+
* Throws HTTPException 401 if invalid
|
|
34
|
+
*/
|
|
35
|
+
export const requireAuth = async (c: Context, next: () => Promise<void>) => {
|
|
36
|
+
if (!validateApiKey(c)) {
|
|
37
|
+
throw new HTTPException(401, { message: 'Invalid API key' });
|
|
38
|
+
}
|
|
39
|
+
await next();
|
|
40
|
+
};
|
package/tsconfig.json
CHANGED