@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.
@@ -1,5 +1,5 @@
1
- import { Context } from "hono";
2
- import { HTTPException } from "hono/http-exception";
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: "Server configuration error" });
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: "Invalid API key" });
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: "ID is required" });
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: "Database unavailable" });
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: "Stream not found" });
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: "error", message: error.message }, error.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
- status: "error",
70
- message: "Internal server error"
71
- }, 500);
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 "hono";
2
- import { addComment } from "../controllers/comment/add.controller";
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("/add", addComment);
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 "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";
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("/", listStreaming);
9
- streamingRouter.post("/", addStreaming);
10
- streamingRouter.delete("/:id", removeStreaming);
8
+ streamingRouter.get('/', listStreaming);
9
+ streamingRouter.post('/', addStreaming);
10
+ streamingRouter.delete('/:id', removeStreaming);
11
11
 
12
12
  export { streamingRouter };
@@ -9,7 +9,7 @@ export type Passkey = {
9
9
  userId: string;
10
10
  publicKey: string; // base64url
11
11
  counter: number;
12
- deviceType: "singleDevice" | "multiDevice";
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
@@ -4,6 +4,6 @@
4
4
  "moduleResolution": "bundler",
5
5
  "jsx": "react-jsx",
6
6
  "jsxImportSource": "hono/jsx",
7
- "types": ["@cloudflare/workers-types", "node"],
8
- },
7
+ "types": ["@cloudflare/workers-types", "node"]
8
+ }
9
9
  }