streamby-core 0.23.5 β†’ 0.24.1

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 CHANGED
@@ -1,206 +1,242 @@
1
- # @streamby/core
2
-
3
- Middleware framework for building storage-agnostic media APIs.
4
- Part of the [StreamBy](https://streamby.nhexa.cl) ecosystem by TerminalCore Labs.
5
-
6
- ---
7
-
8
- ## πŸš€ What is it?
9
-
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
-
12
- - βœ… AWS S3
13
- - βœ… Google Cloud Storage (soon)
14
- - βœ… Cloudflare R2 (soon)
15
- - βœ… Local/Personal servers (soon)
16
-
17
- It's designed to be installed inside your existing API as a library β€” **no need to run a separate backend**.
18
-
19
- ---
20
-
21
- ## πŸ“¦ Installation
22
-
23
- ```bash
24
- npm install @streamby/core
25
- ```
26
-
27
- ---
28
-
29
- ## 🧱 Basic usage
30
-
31
- ```ts
32
- import express from 'express';
33
- import { createStreamByRouter } from '@streamby/core';
34
-
35
- const app = express();
36
-
37
- app.use('/streamby', createStreamByRouter({
38
- storageProvider: {
39
- type: 's3',
40
- config: {
41
- bucket: 'your-bucket-name',
42
- region: 'your-region',
43
- accessKeyId: 'YOUR_AWS_KEY',
44
- secretAccessKey: 'YOUR_AWS_SECRET'
45
- }
46
- },
47
- authProvider: async (req) => {
48
- // extract token/cookie and validate
49
- return {
50
- userId: 'demo',
51
- projects: ['demo-project'],
52
- role: 'admin',
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
- };
64
- }
65
- }));
66
-
67
- app.listen(3000);
68
- ```
69
-
70
- ---
71
-
72
- ## πŸ§ͺ Testing
73
-
74
- ```bash
75
- npm run test
76
- ```
77
-
78
- Unit tests use [Vitest](https://vitest.dev) and [Supertest](https://www.npmjs.com/package/supertest).
79
-
80
- ---
81
-
82
- ## πŸ“ Features
83
-
84
- - πŸ“‚ Multi-project access control
85
- - πŸ” Project-aware file uploads & listings
86
- - 🧹 Modular adapters per storage provider
87
- - 🧰 Built-in testability with mock auth & storage
88
- - ✨ Simple to extend for custom business logic
89
-
90
- ---
91
-
92
- ## πŸ—ΊοΈ API Endpoints
93
-
94
- Here's a summary of the key API endpoints provided by `@streamby/core`:
95
-
96
- ### `GET /streamby/projects`
97
-
98
- Lists all projects accessible by the authenticated user.
99
-
100
- **Query Parameters:**
101
- - `archived`: (Optional) Filter projects by their archived status.
102
- - `true`: Returns only archived projects.
103
- - `false`: Returns only unarchived projects.
104
- - (Omitted): Returns all projects (both archived and unarchived).
105
-
106
- **Example Response:**
107
- ```json
108
- {
109
- "projects": [
110
- {
111
- "id": "project1_id",
112
- "dbType": "nosql",
113
- "name": "My NoSQL Project",
114
- "image": "url_to_image",
115
- "archived": false
116
- },
117
- {
118
- "id": "project2_id",
119
- "dbType": "sql",
120
- "name": "My SQL Project",
121
- "image": "url_to_image",
122
- "archived": true
123
- }
124
- ]
125
- }
126
- ```
127
-
128
- ### `PATCH /streamby/projects/:id/archive`
129
-
130
- Archives a specific project. After archiving, it returns the complete list of projects for the user, including the updated project.
131
-
132
- **Example Response:**
133
- ```json
134
- {
135
- "success": true,
136
- "projects": [
137
- {
138
- "id": "project1_id",
139
- "dbType": "nosql",
140
- "name": "My NoSQL Project",
141
- "image": "url_to_image",
142
- "archived": true
143
- },
144
- {
145
- "id": "project2_id",
146
- "dbType": "sql",
147
- "name": "My SQL Project",
148
- "image": "url_to_image",
149
- "archived": false
150
- }
151
- ]
152
- }
153
- ```
154
-
155
- ### `PATCH /streamby/projects/:id/unarchive`
156
-
157
- Unarchives a specific project. After unarchiving, it returns the complete list of projects for the user, including the updated project.
158
-
159
- **Example Response:**
160
- ```json
161
- {
162
- "success": true,
163
- "projects": [
164
- {
165
- "id": "project1_id",
166
- "dbType": "nosql",
167
- "name": "My NoSQL Project",
168
- "image": "url_to_image",
169
- "archived": false
170
- },
171
- {
172
- "id": "project2_id",
173
- "dbType": "sql",
174
- "name": "My SQL Project",
175
- "image": "url_to_image",
176
- "archived": false
177
- }
178
- ]
179
- }
180
- ```
181
-
182
- ---
183
-
184
- ## 🚣 Roadmap
185
-
186
- - [ ] Support for Google Cloud Storage
187
- - [ ] CLI tool for uploading files
188
- - [ ] Role-based access control middleware
189
- - [ ] Plugin system for extended routes
190
- - [ ] Streaming support (HLS, audio, etc.)
191
-
192
- ---
193
-
194
- ## πŸ§‘β€πŸ’» Maintained by
195
-
196
- **TerminalCore Labs** – part of [Nhexa Entertainment](https://nhexa.cl)
197
- Developed as open-source infrastructure for creative and media-oriented apps.
198
-
199
- Visit us [here](https://terminalcore.cl)
200
-
201
- ---
202
-
203
- ## πŸ“Ÿ License
204
-
205
- [MIT](./LICENSE)
206
-
1
+ # @streamby/core
2
+
3
+ Middleware framework for building storage-agnostic media APIs.
4
+ Part of the [StreamBy](https://streamby.nhexa.cl) ecosystem by TerminalCore Labs.
5
+
6
+ ---
7
+
8
+ ## πŸš€ What is it?
9
+
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
+
12
+ - βœ… AWS S3
13
+ - βœ… Google Cloud Storage (soon)
14
+ - βœ… Cloudflare R2 (soon)
15
+ - βœ… Local/Personal servers (soon)
16
+
17
+ It's designed to be installed inside your existing API as a library β€” **no need to run a separate backend**.
18
+
19
+ ---
20
+
21
+ ## πŸ“¦ Installation
22
+
23
+ ```bash
24
+ npm install @streamby/core
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 🧱 Basic usage
30
+
31
+ ---
32
+
33
+ ## πŸ—„οΈ Database Schema Setup
34
+
35
+ `@streamby/core` requires a pre-configured database schema for its internal operations. It does not manage database connections directly; instead, it expects the host application to provide initialized connection instances. This ensures full control and flexibility for the host environment.
36
+
37
+ The library will only interact with the specified schema and will not touch other schemas in your database. The `reset` option is opt-in and will only drop and recreate the specified `streamby` schema if explicitly set to `true`.
38
+
39
+ ### PostgreSQL Example
40
+
41
+ ```ts
42
+ import { Pool } from "pg";
43
+ import { setupStreambyPg } from "@streamby/core/pg/setup"; // Adjust path as per your package structure
44
+
45
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
46
+
47
+ // Call setupStreambyPg to ensure the schema and tables are created
48
+ // reset: false (default) ensures idempotency and no data loss
49
+ await setupStreambyPg({ pool, schema: "streamby", reset: false });
50
+ ```
51
+
52
+ ### MongoDB Example
53
+
54
+ ```ts
55
+ import { MongoClient } from "mongodb";
56
+ import { setupStreambyMongo } from "@streamby/core/mongo/setup"; // Adjust path as per your package structure
57
+
58
+ const client = new MongoClient(process.env.MONGO_URL!);
59
+
60
+ // Connect the MongoDB client
61
+ await client.connect();
62
+
63
+ // Call setupStreambyMongo to ensure collections and indexes are created
64
+ await setupStreambyMongo({ client, dbName: "streamby" });
65
+ ```
66
+
67
+ ```ts
68
+ import express from 'express';
69
+ import { createStreamByRouter } from '@streamby/core';
70
+
71
+ const app = express();
72
+
73
+ app.use('/streamby', createStreamByRouter({
74
+ storageProvider: {
75
+ type: 's3',
76
+ config: {
77
+ bucket: 'your-bucket-name',
78
+ region: 'your-region',
79
+ accessKeyId: 'YOUR_AWS_KEY',
80
+ secretAccessKey: 'YOUR_AWS_SECRET'
81
+ }
82
+ },
83
+ authProvider: async (req) => {
84
+ // extract token/cookie and validate
85
+ return {
86
+ userId: 'demo',
87
+ projects: ['demo-project'],
88
+ role: 'admin',
89
+ };
90
+ },
91
+ projectProvider: async (projectId) => {
92
+ // fetch project from your DB
93
+ return {
94
+ id: projectId,
95
+ name: 'Demo Project',
96
+ description: 'Demo for StreamBy integration',
97
+ rootFolders: [],
98
+ settings: { allowUpload: true }
99
+ };
100
+ }
101
+ }));
102
+
103
+ app.listen(3000);
104
+ ```
105
+
106
+ ---
107
+
108
+ ## πŸ§ͺ Testing
109
+
110
+ ```bash
111
+ npm run test
112
+ ```
113
+
114
+ Unit tests use [Vitest](https://vitest.dev) and [Supertest](https://www.npmjs.com/package/supertest).
115
+
116
+ ---
117
+
118
+ ## πŸ“ Features
119
+
120
+ - πŸ“‚ Multi-project access control
121
+ - πŸ” Project-aware file uploads & listings
122
+ - 🧹 Modular adapters per storage provider
123
+ - 🧰 Built-in testability with mock auth & storage
124
+ - ✨ Simple to extend for custom business logic
125
+
126
+ ---
127
+
128
+ ## πŸ—ΊοΈ API Endpoints
129
+
130
+ Here's a summary of the key API endpoints provided by `@streamby/core`:
131
+
132
+ ### `GET /streamby/projects`
133
+
134
+ Lists all projects accessible by the authenticated user.
135
+
136
+ **Query Parameters:**
137
+ - `archived`: (Optional) Filter projects by their archived status.
138
+ - `true`: Returns only archived projects.
139
+ - `false`: Returns only unarchived projects.
140
+ - (Omitted): Returns all projects (both archived and unarchived).
141
+
142
+ **Example Response:**
143
+ ```json
144
+ {
145
+ "projects": [
146
+ {
147
+ "id": "project1_id",
148
+ "dbType": "nosql",
149
+ "name": "My NoSQL Project",
150
+ "image": "url_to_image",
151
+ "archived": false
152
+ },
153
+ {
154
+ "id": "project2_id",
155
+ "dbType": "sql",
156
+ "name": "My SQL Project",
157
+ "image": "url_to_image",
158
+ "archived": true
159
+ }
160
+ ]
161
+ }
162
+ ```
163
+
164
+ ### `PATCH /streamby/projects/:id/archive`
165
+
166
+ Archives a specific project. After archiving, it returns the complete list of projects for the user, including the updated project.
167
+
168
+ **Example Response:**
169
+ ```json
170
+ {
171
+ "success": true,
172
+ "projects": [
173
+ {
174
+ "id": "project1_id",
175
+ "dbType": "nosql",
176
+ "name": "My NoSQL Project",
177
+ "image": "url_to_image",
178
+ "archived": true
179
+ },
180
+ {
181
+ "id": "project2_id",
182
+ "dbType": "sql",
183
+ "name": "My SQL Project",
184
+ "image": "url_to_image",
185
+ "archived": false
186
+ }
187
+ ]
188
+ }
189
+ ```
190
+
191
+ ### `PATCH /streamby/projects/:id/unarchive`
192
+
193
+ Unarchives a specific project. After unarchiving, it returns the complete list of projects for the user, including the updated project.
194
+
195
+ **Example Response:**
196
+ ```json
197
+ {
198
+ "success": true,
199
+ "projects": [
200
+ {
201
+ "id": "project1_id",
202
+ "dbType": "nosql",
203
+ "name": "My NoSQL Project",
204
+ "image": "url_to_image",
205
+ "archived": false
206
+ },
207
+ {
208
+ "id": "project2_id",
209
+ "dbType": "sql",
210
+ "name": "My SQL Project",
211
+ "image": "url_to_image",
212
+ "archived": false
213
+ }
214
+ ]
215
+ }
216
+ ```
217
+
218
+ ---
219
+
220
+ ## 🚣 Roadmap
221
+
222
+ - [ ] Support for Google Cloud Storage
223
+ - [ ] CLI tool for uploading files
224
+ - [ ] Role-based access control middleware
225
+ - [ ] Plugin system for extended routes
226
+ - [ ] Streaming support (HLS, audio, etc.)
227
+
228
+ ---
229
+
230
+ ## πŸ§‘β€πŸ’» Maintained by
231
+
232
+ **TerminalCore Labs** – part of [Nhexa Entertainment](https://nhexa.cl)
233
+ Developed as open-source infrastructure for creative and media-oriented apps.
234
+
235
+ Visit us [here](https://terminalcore.cl)
236
+
237
+ ---
238
+
239
+ ## πŸ“Ÿ License
240
+
241
+ [MIT](./LICENSE)
242
+
@@ -44,8 +44,9 @@ exports.getConnection = getConnection;
44
44
  const ensureTablesExist = async (pool) => {
45
45
  try {
46
46
  await pool.query(`
47
+ CREATE SCHEMA IF NOT EXISTS streamby;
47
48
  CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
48
- CREATE TABLE IF NOT EXISTS projects (
49
+ CREATE TABLE IF NOT EXISTS streamby.projects (
49
50
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
50
51
  name VARCHAR(255) NOT NULL,
51
52
  description TEXT,
@@ -57,21 +58,21 @@ const ensureTablesExist = async (pool) => {
57
58
  console.log('βœ… "projects" table ensured to exist.');
58
59
  // Ensure dbType column exists and is correctly configured
59
60
  await pool.query(`
60
- ALTER TABLE projects ADD COLUMN IF NOT EXISTS "dbType" VARCHAR(50);
61
+ ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbType" VARCHAR(50);
61
62
  `);
62
63
  await pool.query(`
63
- UPDATE projects SET "dbType" = 'nosql' WHERE "dbType" IS NULL;
64
+ UPDATE streamby.projects SET "dbType" = 'nosql' WHERE "dbType" IS NULL;
64
65
  `);
65
66
  await pool.query(`
66
- ALTER TABLE projects ALTER COLUMN "dbType" SET NOT NULL;
67
+ ALTER TABLE streamby.projects ALTER COLUMN "dbType" SET NOT NULL;
67
68
  `);
68
69
  await pool.query(`
69
- ALTER TABLE projects ALTER COLUMN "dbType" SET DEFAULT 'nosql';
70
+ ALTER TABLE streamby.projects ALTER COLUMN "dbType" SET DEFAULT 'nosql';
70
71
  `);
71
72
  await pool.query(`
72
- CREATE TABLE IF NOT EXISTS exports (
73
+ CREATE TABLE IF NOT EXISTS streamby.exports (
73
74
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
74
- "projectId" UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
75
+ "projectId" UUID NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
75
76
  status VARCHAR(50) NOT NULL DEFAULT 'pending',
76
77
  "filePath" TEXT,
77
78
  "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
@@ -80,9 +81,9 @@ const ensureTablesExist = async (pool) => {
80
81
  `);
81
82
  console.log('βœ… "exports" table ensured to exist.');
82
83
  await pool.query(`
83
- CREATE TABLE IF NOT EXISTS project_members (
84
+ CREATE TABLE IF NOT EXISTS streamby.project_members (
84
85
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
85
- "projectId" UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
86
+ "projectId" UUID NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
86
87
  "userId" VARCHAR(255) NOT NULL,
87
88
  role VARCHAR(255) NOT NULL DEFAULT 'member',
88
89
  archived BOOLEAN NOT NULL DEFAULT FALSE,
@@ -10,7 +10,7 @@ export declare const createSQLRawExportTable: (connection: Pool, projectId: stri
10
10
  }>;
11
11
  export declare const sqlAdapter: {
12
12
  find: (connection: Pool, tableName: string, filter: any) => Promise<any[]>;
13
- findOne: (connection: Pool, tableName: string, filter: any) => Promise<any | null>;
13
+ findOne: (connection: Pool, tableName: string, filter: any, schema?: string) => Promise<any | null>;
14
14
  create: (connection: Pool, tableName: string, data: any) => Promise<any>;
15
15
  update: (connection: Pool, tableName: string, filter: any, data: any) => Promise<any | null>;
16
16
  delete: (connection: Pool, tableName: string, filter: any) => Promise<number>;
@@ -55,11 +55,12 @@ exports.sqlAdapter = {
55
55
  const result = await connection.query(query, values);
56
56
  return result.rows;
57
57
  },
58
- findOne: async (connection, tableName, filter) => {
58
+ findOne: async (connection, tableName, filter, schema) => {
59
59
  const keys = Object.keys(filter);
60
60
  const values = Object.values(filter);
61
- const where = keys.map((key, i) => `"${key}" = $${i + 1}`).join(' AND ');
62
- const result = await connection.query(`SELECT * FROM "${tableName}" WHERE ${where} LIMIT 1`, values);
61
+ const where = keys.map((key, i) => `"${key}" = ${i + 1}`).join(' AND ');
62
+ const fullTableName = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
63
+ const result = await connection.query(`SELECT * FROM ${fullTableName} WHERE ${where} LIMIT 1`, values);
63
64
  return result.rows[0] || null;
64
65
  },
65
66
  create: async (connection, tableName, data) => {
@@ -0,0 +1,18 @@
1
+ import { Pool } from 'pg';
2
+ import { MongoClient } from 'mongodb';
3
+ export interface SetupStreambyPgOptions {
4
+ pool: Pool;
5
+ schema?: string;
6
+ reset?: boolean;
7
+ allowResetInProd?: boolean;
8
+ }
9
+ export interface SetupStreambyMongoOptions {
10
+ client: MongoClient;
11
+ dbName?: string;
12
+ }
13
+ export interface SetupResult {
14
+ didCreateSchema: boolean;
15
+ didCreateTables: boolean;
16
+ didReset: boolean;
17
+ errors: string[];
18
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -58,7 +58,7 @@ function projectRouter(config) {
58
58
  return res.status(500).json({ message: 'Main database not configured' });
59
59
  }
60
60
  const userDbType = mainDb.type;
61
- const User = (0, manager_1.getModel)('users', userDbType);
61
+ const User = (0, manager_1.getModel)('users', userDbType, 'accounts');
62
62
  const user = await User.findOne({ _id: auth.userId });
63
63
  if (!user) {
64
64
  return res.status(404).json({ message: 'User not found' });
@@ -80,6 +80,7 @@ function projectRouter(config) {
80
80
  res.status(200).json({ success: true, projects: projects, projectId: newProject._id || newProject.id, message: 'Project created successfully' });
81
81
  }
82
82
  catch (err) {
83
+ console.error('Error creating project:', err);
83
84
  res.status(500).json({ message: 'Failed to create project', details: err.message });
84
85
  }
85
86
  });
@@ -248,7 +249,7 @@ function projectRouter(config) {
248
249
  return res.status(500).json({ message: 'Main database not configured' });
249
250
  }
250
251
  const userDbType = mainDb.type;
251
- const User = (0, manager_1.getModel)('users', userDbType);
252
+ const User = (0, manager_1.getModel)('users', userDbType, 'accounts');
252
253
  const membersWithUsernames = await Promise.all(project.members.map(async (member) => {
253
254
  const user = await User.findOne({ _id: member.userId });
254
255
  return {
@@ -2,7 +2,10 @@ import { Document } from 'mongodb';
2
2
  export declare class Model<T extends Document> {
3
3
  private connectionIds;
4
4
  private tableName;
5
- constructor(connectionIds: string[], tableName: string);
5
+ private schema?;
6
+ constructor(connectionIds: string[], tableName: string, schema?: string);
7
+ getConnectionIds(): string[];
8
+ getTableName(): string;
6
9
  useDbType(dbType: string): Model<T>;
7
10
  find(filter: any): Promise<T[]>;
8
11
  private transformResult;
@@ -6,9 +6,17 @@ const mongodb_1 = require("mongodb");
6
6
  const sql_1 = require("../adapters/database/sql");
7
7
  const nosql_1 = require("../adapters/database/nosql");
8
8
  class Model {
9
- constructor(connectionIds, tableName) {
9
+ constructor(connectionIds, tableName, schema) {
10
10
  this.connectionIds = connectionIds;
11
11
  this.tableName = tableName;
12
+ this.schema = schema;
13
+ }
14
+ // Add these getter methods
15
+ getConnectionIds() {
16
+ return this.connectionIds;
17
+ }
18
+ getTableName() {
19
+ return this.tableName;
12
20
  }
13
21
  useDbType(dbType) {
14
22
  const filteredConnectionIds = this.connectionIds.filter(id => {
@@ -34,11 +42,11 @@ class Model {
34
42
  if (this.tableName === 'projects' && processedFilter.members && processedFilter.members.$elemMatch) {
35
43
  // Handle members filter for SQL projects
36
44
  const userId = processedFilter.members.$elemMatch.userId;
37
- const query = `
38
- SELECT p.*, pm."userId" as memberUserId, pm.archived as memberArchived
39
- FROM "projects" p
40
- JOIN "project_members" pm ON p.id = pm."projectId"
41
- WHERE pm."userId" = $1
45
+ const query = `
46
+ SELECT p.*, pm."userId" as memberUserId, pm.archived as memberArchived
47
+ FROM "projects" p
48
+ JOIN "project_members" pm ON p.id = pm."projectId"
49
+ WHERE pm."userId" = $1
42
50
  `;
43
51
  const result = await connection.query(query, [userId]);
44
52
  sqlResults = result.rows.map(row => ({
@@ -96,7 +104,14 @@ class Model {
96
104
  processedFilter.id = processedFilter._id;
97
105
  delete processedFilter._id;
98
106
  }
99
- const result = await sql_1.sqlAdapter.findOne(connection, this.tableName, processedFilter);
107
+ // Special handling for 'users' table with raw query
108
+ if (this.tableName === 'users' && processedFilter.id) {
109
+ const schema = this.schema || 'public'; // Default to 'public' if schema not provided
110
+ const query = `SELECT * FROM "${schema}"."${this.tableName}" WHERE "id" = $1::uuid LIMIT 1`;
111
+ const result = await connection.query(query, [processedFilter.id]);
112
+ return result.rows[0] || null;
113
+ }
114
+ const result = await sql_1.sqlAdapter.findOne(connection, this.tableName, processedFilter, this.schema); // Pass this.schema
100
115
  if (result) {
101
116
  const transformedResult = this.transformResult(result);
102
117
  if (this.tableName === 'projects') {
@@ -1,3 +1,3 @@
1
1
  import { Model } from './Model';
2
- export declare const defineModel: (name: string, connectionIds: string[], tableName: string) => void;
3
- export declare const getModel: (name: string, dbType?: string) => Model<any>;
2
+ export declare const defineModel: (name: string, connectionIds: string[], tableName: string, schema?: string) => void;
3
+ export declare const getModel: (name: string, dbType?: string, schema?: string) => Model<any>;
@@ -3,15 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getModel = exports.defineModel = void 0;
4
4
  const Model_1 = require("./Model");
5
5
  const models = {};
6
- const defineModel = (name, connectionIds, tableName) => {
7
- models[name] = new Model_1.Model(connectionIds, tableName);
6
+ const defineModel = (name, connectionIds, tableName, schema) => {
7
+ models[name] = new Model_1.Model(connectionIds, tableName, schema);
8
8
  };
9
9
  exports.defineModel = defineModel;
10
- const getModel = (name, dbType) => {
10
+ const getModel = (name, dbType, schema) => {
11
11
  const model = models[name];
12
12
  if (!model) {
13
13
  throw new Error(`Model ${name} not defined.`);
14
14
  }
15
+ // If a schema is provided, create a new Model instance with the specified schema
16
+ if (schema) {
17
+ return new Model_1.Model(model.getConnectionIds(), model.getTableName(), schema); // Use getters
18
+ }
15
19
  if (dbType) {
16
20
  return model.useDbType(dbType);
17
21
  }
@@ -0,0 +1,2 @@
1
+ import { SetupStreambyMongoOptions } from '../config/types';
2
+ export declare function setupStreambyMongo({ client, dbName, }: SetupStreambyMongoOptions): Promise<void>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupStreambyMongo = setupStreambyMongo;
4
+ async function setupStreambyMongo({ client, dbName = 'streamby', }) {
5
+ const db = client.db(dbName);
6
+ // Create collections if they don't exist. createCollection is idempotent.
7
+ // It will not recreate the collection if it already exists.
8
+ await db.createCollection('projects');
9
+ await db.createCollection('assets');
10
+ // Create indexes idempotently. createIndex will not recreate if an index
11
+ // with the same key and options already exists.
12
+ // projects collection indexes
13
+ // { name: 1 } (unique opcional si aplica) - starting with non-unique as it's safer
14
+ await db.collection('projects').createIndex({ name: 1 });
15
+ // assets collection indexes
16
+ await db.collection('assets').createIndex({ projectId: 1 });
17
+ await db.collection('assets').createIndex({ path: 1 });
18
+ console.log(`MongoDB setup complete for database: ${dbName}`);
19
+ }
@@ -0,0 +1,2 @@
1
+ import { SetupStreambyPgOptions, SetupResult } from '../config/types';
2
+ export declare function setupStreambyPg({ pool, schema, reset, allowResetInProd, }: SetupStreambyPgOptions): Promise<SetupResult>;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupStreambyPg = setupStreambyPg;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ async function setupStreambyPg({ pool, schema = 'streamby', reset = false, allowResetInProd = false, // New parameter
7
+ }) {
8
+ const client = await pool.connect();
9
+ const lockKey = `streamby:setup:${schema}`;
10
+ const lockHash = `hashtext('${lockKey}')`;
11
+ const result = {
12
+ didCreateSchema: false,
13
+ didCreateTables: false,
14
+ didReset: false,
15
+ errors: [],
16
+ };
17
+ try {
18
+ // Production safety check
19
+ if (process.env.NODE_ENV === 'production' && reset && !allowResetInProd) {
20
+ const errorMessage = `Attempted to reset schema '${schema}' in production environment without explicit 'allowResetInProd' flag. Aborting to prevent data loss.`;
21
+ result.errors.push(errorMessage);
22
+ throw new Error(errorMessage);
23
+ }
24
+ const lockResult = await client.query(`SELECT pg_try_advisory_lock(${lockHash})::boolean AS locked;`);
25
+ if (!lockResult.rows[0].locked) {
26
+ const errorMessage = `Another setup process for schema '${schema}' is already running. Could not acquire advisory lock.`;
27
+ result.errors.push(errorMessage);
28
+ throw new Error(errorMessage);
29
+ }
30
+ await client.query('BEGIN;');
31
+ let fullSql = '';
32
+ // Read and append reset SQL if reset is true
33
+ if (reset) {
34
+ console.log(`[StreamByPgSetup] Resetting schema "${schema}"...`);
35
+ const resetSqlPath = (0, node_path_1.join)(__dirname, '../../sql/reset.sql');
36
+ const resetSql = await node_fs_1.promises.readFile(resetSqlPath, 'utf8');
37
+ fullSql += resetSql;
38
+ result.didReset = true;
39
+ result.didCreateSchema = true; // Schema is recreated during reset
40
+ }
41
+ else {
42
+ console.log(`[StreamByPgSetup] Ensuring schema "${schema}" exists...`);
43
+ // If not resetting, the CREATE SCHEMA IF NOT EXISTS in setup.sql will handle it.
44
+ // We can assume it will attempt to create if not exists.
45
+ result.didCreateSchema = true;
46
+ }
47
+ // Read and append setup SQL
48
+ console.log(`[StreamByPgSetup] Creating/updating tables and indexes in schema "${schema}"...`);
49
+ const setupSqlPath = (0, node_path_1.join)(__dirname, '../../sql/setup.sql');
50
+ const setupSql = await node_fs_1.promises.readFile(setupSqlPath, 'utf8');
51
+ fullSql += setupSql;
52
+ result.didCreateTables = true; // Assuming if setup.sql runs, tables are handled
53
+ // Replace {{SCHEMA}} token with the actual schema name, ensuring proper quoting
54
+ const finalSql = fullSql.replace(/{{SCHEMA}}/g, `"${schema}"`);
55
+ await client.query(finalSql);
56
+ await client.query('COMMIT;');
57
+ console.log(`[StreamByPgSetup] Schema setup for "${schema}" completed successfully.`);
58
+ }
59
+ catch (error) {
60
+ await client.query('ROLLBACK;');
61
+ const errorMessage = error instanceof Error ? error.message : String(error);
62
+ result.errors.push(errorMessage);
63
+ console.error(`[StreamByPgSetup] Error during schema setup for "${schema}":`, error);
64
+ throw error; // Re-throw the error after logging and capturing
65
+ }
66
+ finally {
67
+ await client.query(`SELECT pg_advisory_unlock(${lockHash});`);
68
+ client.release();
69
+ }
70
+ return result;
71
+ }
package/package.json CHANGED
@@ -1,60 +1,60 @@
1
- {
2
- "name": "streamby-core",
3
- "version": "0.23.5",
4
- "description": "StreamBy middleware framework for media storage management",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "files": [
8
- "dist"
9
- ],
10
- "scripts": {
11
- "build": "tsc",
12
- "test": "vitest",
13
- "dev": "ts-node-dev --respawn --clear dev.ts",
14
- "prepublishOnly": "npm run build"
15
- },
16
- "repository": {
17
- "type": "git",
18
- "url": "https://github.com/GwerhDev/StreamBy-Package.git"
19
- },
20
- "keywords": [
21
- "streamby",
22
- "middleware",
23
- "upload",
24
- "storage",
25
- "s3",
26
- "express",
27
- "typescript"
28
- ],
29
- "author": "TerminalCore Labs",
30
- "license": "MIT",
31
- "dependencies": {
32
- "@aws-sdk/client-s3": "^3.528.0",
33
- "@aws-sdk/s3-request-presigner": "^3.787.0",
34
- "@prisma/client": "^6.12.0",
35
- "bcrypt": "^5.1.1",
36
- "dotenv": "^16.5.0",
37
- "express": "^4.0.0",
38
- "mongodb": "^6.17.0",
39
- "mongoose": "^8.13.2",
40
- "pg": "^8.16.3",
41
- "prisma": "^6.12.0"
42
- },
43
- "devDependencies": {
44
- "@types/bcrypt": "^5.0.2",
45
- "@types/cors": "^2.8.18",
46
- "@types/express": "^4.17.17",
47
- "@types/mongodb": "^4.0.7",
48
- "@types/node": "^22.15.21",
49
- "@types/pg": "^8.15.4",
50
- "@types/supertest": "^6.0.3",
51
- "cors": "^2.8.5",
52
- "supertest": "^7.1.4",
53
- "ts-node-dev": "^2.0.0",
54
- "typescript": "^5.8.3",
55
- "vitest": "^3.1.1"
56
- },
57
- "peerDependencies": {
58
- "express": "^4.0.0"
59
- }
60
- }
1
+ {
2
+ "name": "streamby-core",
3
+ "version": "0.24.1",
4
+ "description": "StreamBy middleware framework for media storage management",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "vitest",
13
+ "dev": "ts-node-dev --respawn --clear dev.ts",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/GwerhDev/StreamBy-Package.git"
19
+ },
20
+ "keywords": [
21
+ "streamby",
22
+ "middleware",
23
+ "upload",
24
+ "storage",
25
+ "s3",
26
+ "express",
27
+ "typescript"
28
+ ],
29
+ "author": "TerminalCore Labs",
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "@aws-sdk/client-s3": "^3.528.0",
33
+ "@aws-sdk/s3-request-presigner": "^3.787.0",
34
+ "@prisma/client": "^6.12.0",
35
+ "bcrypt": "^5.1.1",
36
+ "dotenv": "^16.5.0",
37
+ "express": "^4.0.0",
38
+ "mongodb": "^6.17.0",
39
+ "mongoose": "^8.13.2",
40
+ "pg": "^8.16.3",
41
+ "prisma": "^6.12.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/bcrypt": "^5.0.2",
45
+ "@types/cors": "^2.8.18",
46
+ "@types/express": "^4.17.17",
47
+ "@types/mongodb": "^4.0.7",
48
+ "@types/node": "^22.15.21",
49
+ "@types/pg": "^8.15.4",
50
+ "@types/supertest": "^6.0.3",
51
+ "cors": "^2.8.5",
52
+ "supertest": "^7.1.4",
53
+ "ts-node-dev": "^2.0.0",
54
+ "typescript": "^5.8.3",
55
+ "vitest": "^3.1.1"
56
+ },
57
+ "peerDependencies": {
58
+ "express": "^4.0.0"
59
+ }
60
+ }