streamby-core 0.1.0 โ 0.2.0
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 +18 -7
- package/dist/adapters/s3.js +8 -4
- package/dist/middleware/createRouter.js +24 -2
- package/dist/services/auth.js +5 -1
- package/dist/types/index.d.ts +19 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Part of the [StreamBy](https://streamby.nhexa.cl) ecosystem by TerminalCore Labs
|
|
|
7
7
|
|
|
8
8
|
## ๐ What is it?
|
|
9
9
|
|
|
10
|
-
`@streamby/core` is a plug-and-play middleware for Express (or compatible frameworks) that enables file uploads, listings and project
|
|
10
|
+
`@streamby/core` is a plug-and-play middleware for Express (or compatible frameworks) that enables file uploads, listings and **multi-project access** over services like:
|
|
11
11
|
|
|
12
12
|
- โ
AWS S3
|
|
13
13
|
- โ
Google Cloud Storage (soon)
|
|
@@ -45,12 +45,22 @@ app.use('/streamby', createStreamByRouter({
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
authProvider: async (req) => {
|
|
48
|
-
//
|
|
48
|
+
// extract token/cookie and validate
|
|
49
49
|
return {
|
|
50
50
|
userId: 'demo',
|
|
51
|
-
|
|
51
|
+
projects: ['demo-project'],
|
|
52
52
|
role: 'admin',
|
|
53
53
|
};
|
|
54
|
+
},
|
|
55
|
+
projectProvider: async (projectId) => {
|
|
56
|
+
// fetch project from your DB
|
|
57
|
+
return {
|
|
58
|
+
id: projectId,
|
|
59
|
+
name: 'Demo Project',
|
|
60
|
+
description: 'Demo for StreamBy integration',
|
|
61
|
+
rootFolders: [],
|
|
62
|
+
settings: { allowUpload: true }
|
|
63
|
+
};
|
|
54
64
|
}
|
|
55
65
|
}));
|
|
56
66
|
|
|
@@ -71,11 +81,11 @@ Unit tests use [Vitest](https://vitest.dev) and [Supertest](https://www.npmjs.co
|
|
|
71
81
|
|
|
72
82
|
## ๐ Features
|
|
73
83
|
|
|
84
|
+
- ๐ Multi-project access control
|
|
85
|
+
- ๐ Project-aware file uploads & listings
|
|
74
86
|
- ๐งน Modular adapters per storage provider
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
- ๐งฐ Built-in testability with mock adapters
|
|
78
|
-
- โก Easy to extend and compose
|
|
87
|
+
- ๐งฐ Built-in testability with mock auth & storage
|
|
88
|
+
- โจ Simple to extend for custom business logic
|
|
79
89
|
|
|
80
90
|
---
|
|
81
91
|
|
|
@@ -85,6 +95,7 @@ Unit tests use [Vitest](https://vitest.dev) and [Supertest](https://www.npmjs.co
|
|
|
85
95
|
- [ ] CLI tool for uploading files
|
|
86
96
|
- [ ] Role-based access control middleware
|
|
87
97
|
- [ ] Plugin system for extended routes
|
|
98
|
+
- [ ] Streaming support (HLS, audio, etc.)
|
|
88
99
|
|
|
89
100
|
---
|
|
90
101
|
|
package/dist/adapters/s3.js
CHANGED
|
@@ -12,19 +12,23 @@ function createS3Adapter(config) {
|
|
|
12
12
|
});
|
|
13
13
|
return {
|
|
14
14
|
async uploadFile(req, projectId) {
|
|
15
|
-
const file = req.body
|
|
15
|
+
const file = req.body?.file;
|
|
16
|
+
if (!file || !file.name || !file.buffer) {
|
|
17
|
+
throw new Error('Invalid file upload: missing name or buffer');
|
|
18
|
+
}
|
|
19
|
+
const key = `${projectId}/${file.name}`;
|
|
16
20
|
const command = new client_s3_1.PutObjectCommand({
|
|
17
21
|
Bucket: config.bucket,
|
|
18
|
-
Key:
|
|
22
|
+
Key: key,
|
|
19
23
|
Body: file.buffer,
|
|
20
24
|
});
|
|
21
25
|
await s3.send(command);
|
|
22
|
-
return { success: true, key
|
|
26
|
+
return { success: true, key };
|
|
23
27
|
},
|
|
24
28
|
async listFiles(projectId) {
|
|
25
29
|
const command = new client_s3_1.ListObjectsV2Command({
|
|
26
30
|
Bucket: config.bucket,
|
|
27
|
-
Prefix: `${projectId}
|
|
31
|
+
Prefix: `${projectId}/`,
|
|
28
32
|
});
|
|
29
33
|
const result = await s3.send(command);
|
|
30
34
|
return result.Contents || [];
|
|
@@ -20,7 +20,11 @@ function createStreamByRouter(config) {
|
|
|
20
20
|
router.get('/files', async (req, res) => {
|
|
21
21
|
try {
|
|
22
22
|
const auth = await config.authProvider(req);
|
|
23
|
-
const
|
|
23
|
+
const projectId = (req.query.projectId || req.headers['x-project-id']);
|
|
24
|
+
if (!projectId || !auth.projects.includes(projectId)) {
|
|
25
|
+
return res.status(403).json({ error: 'Unauthorized or missing projectId' });
|
|
26
|
+
}
|
|
27
|
+
const files = await (0, file_1.listFilesService)(adapter, req, projectId);
|
|
24
28
|
res.json(files);
|
|
25
29
|
}
|
|
26
30
|
catch (err) {
|
|
@@ -30,12 +34,30 @@ function createStreamByRouter(config) {
|
|
|
30
34
|
router.post('/upload', async (req, res) => {
|
|
31
35
|
try {
|
|
32
36
|
const auth = await config.authProvider(req);
|
|
33
|
-
const
|
|
37
|
+
const projectId = (req.query.projectId || req.headers['x-project-id']);
|
|
38
|
+
if (!projectId || !auth.projects.includes(projectId)) {
|
|
39
|
+
return res.status(403).json({ error: 'Unauthorized or missing projectId' });
|
|
40
|
+
}
|
|
41
|
+
const result = await (0, file_1.uploadFileService)(adapter, req, projectId);
|
|
34
42
|
res.json(result);
|
|
35
43
|
}
|
|
36
44
|
catch (err) {
|
|
37
45
|
res.status(500).json({ error: 'Failed to upload file', details: err });
|
|
38
46
|
}
|
|
39
47
|
});
|
|
48
|
+
router.get('/projects/:id', async (req, res) => {
|
|
49
|
+
try {
|
|
50
|
+
const auth = await config.authProvider(req);
|
|
51
|
+
const projectId = req.params.id;
|
|
52
|
+
if (!projectId || !auth.projects.includes(projectId)) {
|
|
53
|
+
return res.status(403).json({ error: 'Unauthorized project access' });
|
|
54
|
+
}
|
|
55
|
+
const project = await config.projectProvider(projectId);
|
|
56
|
+
res.json({ project });
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
res.status(404).json({ error: 'Project not found', details: err });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
40
62
|
return router;
|
|
41
63
|
}
|
package/dist/services/auth.js
CHANGED
|
@@ -4,7 +4,11 @@ exports.resolveAuth = resolveAuth;
|
|
|
4
4
|
exports.checkRole = checkRole;
|
|
5
5
|
async function resolveAuth(config, req) {
|
|
6
6
|
const auth = await config.authProvider(req);
|
|
7
|
-
if (!auth ||
|
|
7
|
+
if (!auth ||
|
|
8
|
+
!auth.userId ||
|
|
9
|
+
!Array.isArray(auth.projects) ||
|
|
10
|
+
auth.projects.length === 0 ||
|
|
11
|
+
!auth.role) {
|
|
8
12
|
throw new Error('Invalid or missing authentication context');
|
|
9
13
|
}
|
|
10
14
|
return auth;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,20 +10,33 @@ export interface StorageAdapter {
|
|
|
10
10
|
uploadFile(req: Request, projectId: string): Promise<any>;
|
|
11
11
|
listFiles(projectId: string): Promise<any[]>;
|
|
12
12
|
}
|
|
13
|
+
export interface ProjectInfo {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
rootFolders?: FolderNode[];
|
|
18
|
+
settings?: {
|
|
19
|
+
allowUpload?: boolean;
|
|
20
|
+
allowSharing?: boolean;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface FolderNode {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
children?: FolderNode[];
|
|
27
|
+
}
|
|
13
28
|
export interface StreamByConfig {
|
|
14
29
|
storageProvider: {
|
|
15
30
|
type: StorageProviderType;
|
|
16
31
|
config: S3Config;
|
|
17
32
|
};
|
|
18
|
-
authProvider:
|
|
19
|
-
|
|
20
|
-
projectId: string;
|
|
21
|
-
role: 'viewer' | 'editor' | 'admin';
|
|
22
|
-
}>;
|
|
33
|
+
authProvider: AuthProvider;
|
|
34
|
+
projectProvider: ProjectProvider;
|
|
23
35
|
}
|
|
24
36
|
export interface AuthContext {
|
|
25
37
|
userId: string;
|
|
26
|
-
|
|
38
|
+
projects: string[];
|
|
27
39
|
role: 'viewer' | 'editor' | 'admin';
|
|
28
40
|
}
|
|
29
41
|
export type AuthProvider = (req: Request) => Promise<AuthContext>;
|
|
42
|
+
export type ProjectProvider = (projectId: string) => Promise<ProjectInfo>;
|