saas-backend-kit 1.0.0 → 1.0.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 +117 -370
- package/copy-dts.js +59 -0
- package/dist/auth/index.js +6 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +6 -1
- package/dist/auth/index.mjs.map +1 -1
- package/dist/config/index.js +6 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/index.mjs +6 -1
- package/dist/config/index.mjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +184 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +183 -2
- package/dist/index.mjs.map +1 -1
- package/dist/logger/index.js +6 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/logger/index.mjs +6 -1
- package/dist/logger/index.mjs.map +1 -1
- package/dist/notifications/index.js +6 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/index.mjs +6 -1
- package/dist/notifications/index.mjs.map +1 -1
- package/dist/queue/index.js +6 -1
- package/dist/queue/index.js.map +1 -1
- package/dist/queue/index.mjs +6 -1
- package/dist/queue/index.mjs.map +1 -1
- package/dist/rate-limit/index.js +6 -1
- package/dist/rate-limit/index.js.map +1 -1
- package/dist/rate-limit/index.mjs +6 -1
- package/dist/rate-limit/index.mjs.map +1 -1
- package/dist/upload/index.d.ts +57 -0
- package/dist/upload/index.js +344 -0
- package/dist/upload/index.js.map +1 -0
- package/dist/upload/index.mjs +334 -0
- package/dist/upload/index.mjs.map +1 -0
- package/package.json +12 -2
- package/saas-banner.svg +239 -0
- package/src/config/index.ts +5 -0
- package/src/index.ts +2 -0
- package/src/upload/index.ts +268 -0
- package/tsup.config.ts +2 -1
package/README.md
CHANGED
|
@@ -1,459 +1,206 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<!-- ANIMATED PIPELINE BANNER -->
|
|
4
|
+
<img src="./saas-banner.svg" width="100%" alt="saas-backend-kit — Production-grade modular backend toolkit for Node.js, Express, Fastify"/>
|
|
5
|
+
|
|
6
|
+
<br/>
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/saas-backend-kit)
|
|
9
|
+
[](https://www.npmjs.com/package/saas-backend-kit)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://nodejs.org/)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
| Module | Description | Powered By |
|
|
21
|
+
|--------|-------------|------------|
|
|
22
|
+
| 🔐 **Authentication** | JWT auth, RBAC, Google OAuth | jsonwebtoken, Passport |
|
|
23
|
+
| ⚡ **Task Queue** | Redis-based background jobs | BullMQ |
|
|
24
|
+
| 🔔 **Notifications** | Email, SMS, Webhooks, Slack | Nodemailer, Twilio |
|
|
25
|
+
| ☁️ **File Upload** | Images, videos, docs to S3 | AWS SDK v3 |
|
|
26
|
+
| 📋 **Logger** | Structured JSON logging | Pino |
|
|
27
|
+
| 🛡️ **Rate Limiting** | Configurable API rate limiting | express-rate-limit |
|
|
28
|
+
| ⚙️ **Config Manager** | Env variable validation | Zod |
|
|
29
|
+
| 📦 **API Responses** | Standardized response format | Built-in |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 📦 Installation
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
36
|
npm install saas-backend-kit
|
|
35
37
|
```
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🚀 Quick Start
|
|
38
42
|
|
|
39
43
|
```typescript
|
|
40
44
|
import express from 'express';
|
|
41
|
-
import { auth, rateLimit, logger, config
|
|
45
|
+
import { auth, rateLimit, logger, config } from 'saas-backend-kit';
|
|
42
46
|
|
|
43
47
|
config.load();
|
|
44
48
|
|
|
45
49
|
const app = express();
|
|
46
50
|
app.use(express.json());
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
const authMiddleware = auth.initialize({
|
|
50
|
-
jwtSecret: process.env.JWT_SECRET,
|
|
51
|
-
jwtExpiresIn: '7d',
|
|
52
|
-
});
|
|
53
|
-
app.use(authMiddleware);
|
|
54
|
-
|
|
55
|
-
// Apply rate limiting
|
|
52
|
+
app.use(auth.initialize({ jwtSecret: 'your-secret-key' }));
|
|
56
53
|
app.use(rateLimit({ window: '1m', limit: 100 }));
|
|
57
54
|
|
|
58
|
-
// Protected route
|
|
59
55
|
app.get('/dashboard', auth.requireUser(), (req, res) => {
|
|
60
|
-
res.success({ message: 'Welcome
|
|
56
|
+
res.success({ message: 'Welcome!', user: req.user });
|
|
61
57
|
});
|
|
62
58
|
|
|
63
59
|
app.listen(3000, () => logger.info('Server running'));
|
|
64
60
|
```
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
---
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
## 🔐 Authentication
|
|
69
65
|
|
|
70
66
|
```typescript
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const authMiddleware = auth.initialize({
|
|
67
|
+
app.use(auth.initialize({
|
|
74
68
|
jwtSecret: 'your-secret-key',
|
|
75
|
-
jwtExpiresIn: '7d'
|
|
76
|
-
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
app.use(authMiddleware);
|
|
80
|
-
|
|
81
|
-
// Registration
|
|
82
|
-
app.post('/auth/register', async (req, res) => {
|
|
83
|
-
const { email, password, name } = req.body;
|
|
84
|
-
const result = await auth().register({ email, password, name });
|
|
85
|
-
res.success(result.tokens, 'Registration successful');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Login
|
|
89
|
-
app.post('/auth/login', async (req, res) => {
|
|
90
|
-
const { email, password } = req.body;
|
|
91
|
-
const result = await auth().login({ email, password });
|
|
92
|
-
res.success(result.tokens, 'Login successful');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Route protection
|
|
96
|
-
app.get('/dashboard', auth.requireUser(), (req, res) => {
|
|
97
|
-
res.success({ user: req.user });
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
app.get('/admin', auth.requireRole('admin'), (req, res) => {
|
|
101
|
-
res.success({ message: 'Admin area' });
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
app.get('/protected', auth.requirePermission('read'), (req, res) => {
|
|
105
|
-
res.success({ message: 'Access granted' });
|
|
106
|
-
});
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Role-Based Access Control (RBAC)
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
import { rbacService } from 'saas-backend-kit/auth';
|
|
69
|
+
jwtExpiresIn: '7d'
|
|
70
|
+
}));
|
|
113
71
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
manager: ['read', 'write', 'delete:own'],
|
|
117
|
-
user: ['read', 'write:own'],
|
|
118
|
-
guest: ['read:public'],
|
|
119
|
-
});
|
|
72
|
+
await auth().register({ email, password, name });
|
|
73
|
+
await auth().login({ email, password });
|
|
120
74
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
75
|
+
auth.requireUser();
|
|
76
|
+
auth.requireRole('admin');
|
|
77
|
+
auth.requirePermission('read');
|
|
125
78
|
```
|
|
126
79
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
// Get Google auth URL
|
|
131
|
-
app.get('/auth/google', async (req, res) => {
|
|
132
|
-
const url = await auth().getGoogleAuthUrl();
|
|
133
|
-
res.redirect(url);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Handle callback
|
|
137
|
-
app.get('/auth/google/callback', async (req, res) => {
|
|
138
|
-
const { code } = req.query;
|
|
139
|
-
const result = await auth().handleGoogleCallback(code as string);
|
|
140
|
-
res.success(result.tokens);
|
|
141
|
-
});
|
|
142
|
-
```
|
|
80
|
+
---
|
|
143
81
|
|
|
144
|
-
|
|
82
|
+
## ⚡ Task Queue
|
|
145
83
|
|
|
146
84
|
```typescript
|
|
147
85
|
import { queue } from 'saas-backend-kit';
|
|
148
86
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
defaultJobOptions: {
|
|
152
|
-
attempts: 3,
|
|
153
|
-
backoff: { type: 'exponential', delay: 1000 },
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Add jobs
|
|
158
|
-
await emailQueue.add('sendWelcomeEmail', { userId: '123', email: 'user@example.com' });
|
|
87
|
+
const emailQueue = queue.create('email');
|
|
88
|
+
await emailQueue.add('sendEmail', { to: 'user@example.com' });
|
|
159
89
|
|
|
160
|
-
// Delayed job
|
|
161
|
-
await emailQueue.add('sendReminder', { userId: '123' }, { delay: 3600000 });
|
|
162
|
-
|
|
163
|
-
// Process jobs
|
|
164
90
|
queue.process('email', async (job) => {
|
|
165
|
-
|
|
166
|
-
await notify.email({
|
|
167
|
-
to: job.data.email,
|
|
168
|
-
subject: 'Welcome!',
|
|
169
|
-
template: 'welcome',
|
|
170
|
-
templateData: { name: 'User' },
|
|
171
|
-
});
|
|
172
|
-
return { sent: true };
|
|
91
|
+
await notify.email({ to: job.data.to, subject: 'Hello' });
|
|
173
92
|
}, { concurrency: 5 });
|
|
174
93
|
```
|
|
175
94
|
|
|
176
|
-
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🔔 Notifications
|
|
177
98
|
|
|
178
99
|
```typescript
|
|
179
100
|
import { notify } from 'saas-backend-kit';
|
|
180
101
|
|
|
181
|
-
|
|
182
|
-
await notify.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
template: 'welcome',
|
|
186
|
-
templateData: { name: 'John', appName: 'MyApp' },
|
|
187
|
-
});
|
|
102
|
+
await notify.email({ to: 'user@example.com', subject: 'Welcome' });
|
|
103
|
+
await notify.sms({ to: '+1234567890', message: 'Your code is 123456' });
|
|
104
|
+
await notify.slack({ text: 'New user registered!' });
|
|
105
|
+
```
|
|
188
106
|
|
|
189
|
-
|
|
190
|
-
await notify.sms({
|
|
191
|
-
to: '+1234567890',
|
|
192
|
-
message: 'Your verification code is 123456',
|
|
193
|
-
});
|
|
107
|
+
---
|
|
194
108
|
|
|
195
|
-
|
|
196
|
-
await notify.webhook({
|
|
197
|
-
url: 'https://api.example.com/webhook',
|
|
198
|
-
method: 'POST',
|
|
199
|
-
body: { event: 'user.created', data: { id: '123' } },
|
|
200
|
-
});
|
|
109
|
+
## ☁️ File Upload (S3)
|
|
201
110
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
]
|
|
111
|
+
```typescript
|
|
112
|
+
import { upload } from 'saas-backend-kit';
|
|
113
|
+
|
|
114
|
+
upload.initialize({
|
|
115
|
+
region: 'us-east-1',
|
|
116
|
+
accessKeyId: 'your-access-key',
|
|
117
|
+
secretAccessKey: 'your-secret-key',
|
|
118
|
+
bucket: 'your-bucket-name'
|
|
211
119
|
});
|
|
120
|
+
|
|
121
|
+
const result = await upload.file(fileBuffer, { key: 'documents/file.pdf' });
|
|
122
|
+
const image = await upload.image(fileBuffer, 'photo.jpg');
|
|
123
|
+
const video = await upload.video(fileBuffer, 'video.mp4');
|
|
124
|
+
const signedUrl = await upload.getSignedUrl('path/to/file');
|
|
125
|
+
|
|
126
|
+
await upload.delete('path/to/file');
|
|
212
127
|
```
|
|
213
128
|
|
|
214
|
-
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 📋 Logger
|
|
215
132
|
|
|
216
133
|
```typescript
|
|
217
134
|
import { logger } from 'saas-backend-kit';
|
|
218
135
|
|
|
219
136
|
logger.info('Server started', { port: 3000 });
|
|
220
|
-
logger.error('
|
|
221
|
-
logger.warn('Rate limit approaching', { ip: req.ip });
|
|
137
|
+
logger.error('Failed', { error: err.message });
|
|
222
138
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
childLogger.info('User logged in', { userId: '123' });
|
|
139
|
+
const child = logger.child({ module: 'auth' });
|
|
140
|
+
child.info('User logged in');
|
|
226
141
|
```
|
|
227
142
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
import { rateLimit } from 'saas-backend-kit';
|
|
232
|
-
|
|
233
|
-
// Global rate limit
|
|
234
|
-
app.use(rateLimit({
|
|
235
|
-
window: '1m',
|
|
236
|
-
limit: 100,
|
|
237
|
-
}));
|
|
143
|
+
---
|
|
238
144
|
|
|
239
|
-
|
|
240
|
-
app.get('/api/expensive', rateLimit({ window: '1m', limit: 10 }), (req, res) => {
|
|
241
|
-
res.success({ data: 'expensive operation' });
|
|
242
|
-
});
|
|
145
|
+
## 🛡️ Rate Limiting
|
|
243
146
|
|
|
244
|
-
|
|
245
|
-
app.use(rateLimit({
|
|
246
|
-
|
|
247
|
-
limit: 100,
|
|
248
|
-
keyGenerator: (req) => req.user?.id || req.ip,
|
|
249
|
-
}));
|
|
147
|
+
```typescript
|
|
148
|
+
app.use(rateLimit({ window: '1m', limit: 100 }));
|
|
149
|
+
app.use(rateLimit({ window: '1m', limit: 10, keyGenerator: (req) => req.user?.id }));
|
|
250
150
|
```
|
|
251
151
|
|
|
252
|
-
|
|
152
|
+
---
|
|
253
153
|
|
|
254
|
-
|
|
255
|
-
import { config } from 'saas-backend-kit';
|
|
154
|
+
## ⚙️ Config
|
|
256
155
|
|
|
257
|
-
|
|
156
|
+
```typescript
|
|
258
157
|
config.load();
|
|
259
|
-
|
|
260
|
-
// Get values
|
|
261
|
-
const port = config.int('PORT');
|
|
158
|
+
const port = config.int('PORT');
|
|
262
159
|
const isProduction = config.isProduction();
|
|
263
|
-
const dbUrl = config.get('DATABASE_URL');
|
|
264
|
-
|
|
265
|
-
// Custom schema
|
|
266
|
-
const customConfig = config.create({
|
|
267
|
-
schema: z.object({
|
|
268
|
-
API_KEY: z.string(),
|
|
269
|
-
MAX_CONNECTIONS: z.number().default(100),
|
|
270
|
-
}),
|
|
271
|
-
validate: true,
|
|
272
|
-
});
|
|
273
160
|
```
|
|
274
161
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
// Success responses
|
|
279
|
-
res.success({ user }, 'User found');
|
|
280
|
-
res.created({ id: '123' }, 'User created');
|
|
281
|
-
res.updated(user, 'User updated');
|
|
282
|
-
res.deleted Paginated response('User deleted');
|
|
283
|
-
|
|
284
|
-
//
|
|
285
|
-
res.pag, limit, total);
|
|
286
|
-
|
|
287
|
-
// Error responses
|
|
288
|
-
inated(users, pageres.badInvalid inputauthorized('Please');
|
|
289
|
-
res.unRequest(' login');
|
|
290
|
-
res.forbidden('Access denied');
|
|
291
|
-
res.notFound('User not found');
|
|
292
|
-
res failed', { fields.validationError('Validation: errors('Something });
|
|
293
|
-
res.internalError went wrong');
|
|
294
|
-
```
|
|
162
|
+
---
|
|
295
163
|
|
|
296
|
-
|
|
164
|
+
## 📦 API Responses
|
|
297
165
|
|
|
298
166
|
```typescript
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
initialize: (app) => {
|
|
304
|
-
console.log('Plugin initialized');
|
|
305
|
-
},
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
const app = createApp({
|
|
309
|
-
auth: true,
|
|
310
|
-
queue: { redisUrl: 'redis://localhost:6379' },
|
|
311
|
-
notifications: true,
|
|
312
|
-
rateLimit: { window: '1m', limit: 100 },
|
|
313
|
-
})
|
|
314
|
-
.use(myPlugin);
|
|
315
|
-
|
|
316
|
-
await app.initialize(expressApp);
|
|
167
|
+
res.success({ user });
|
|
168
|
+
res.created(user, 'Created');
|
|
169
|
+
res.paginated(users, page, limit, total);
|
|
170
|
+
res.error('Error message');
|
|
317
171
|
```
|
|
318
172
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
### Express
|
|
322
|
-
|
|
323
|
-
All examples above use Express. The library automatically extends the Response object with helper methods.
|
|
324
|
-
|
|
325
|
-
### Fastify
|
|
326
|
-
|
|
327
|
-
```typescript
|
|
328
|
-
import Fastify from 'fastify';
|
|
329
|
-
import { registerAuthPlugin, registerRateLimitPlugin, auth } from 'saas-backend-kit';
|
|
330
|
-
|
|
331
|
-
const fastify = Fastify();
|
|
332
|
-
|
|
333
|
-
fastify.register(registerAuthPlugin, { authService: auth() });
|
|
334
|
-
fastify.register(registerRateLimitPlugin, { window: '1m', limit: 100 });
|
|
335
|
-
|
|
336
|
-
fastify.get('/dashboard', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
337
|
-
return { user: request.user };
|
|
338
|
-
});
|
|
339
|
-
```
|
|
173
|
+
---
|
|
340
174
|
|
|
341
|
-
## Environment Variables
|
|
175
|
+
## 🔧 Environment Variables
|
|
342
176
|
|
|
343
177
|
```env
|
|
344
178
|
NODE_ENV=development
|
|
345
179
|
PORT=3000
|
|
346
|
-
|
|
347
|
-
# JWT
|
|
348
|
-
JWT_SECRET=your-secret-key-min-32-chars
|
|
349
|
-
JWT_EXPIRES_IN=7d
|
|
350
|
-
JWT_REFRESH_SECRET=your-refresh-secret
|
|
351
|
-
JWT_REFRESH_EXPIRES_IN=30d
|
|
352
|
-
|
|
353
|
-
# Redis
|
|
180
|
+
JWT_SECRET=your-secret-key
|
|
354
181
|
REDIS_URL=redis://localhost:6379
|
|
355
182
|
|
|
356
|
-
#
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
# SMTP
|
|
362
|
-
SMTP_HOST=smtp.example.com
|
|
363
|
-
SMTP_PORT=587
|
|
364
|
-
SMTP_USER=
|
|
365
|
-
SMTP_PASS=
|
|
366
|
-
SMTP_FROM=noreply@domain.com
|
|
367
|
-
|
|
368
|
-
# Twilio
|
|
369
|
-
TWILIO_ACCOUNT_SID=
|
|
370
|
-
TWILIO_AUTH_TOKEN=
|
|
371
|
-
TWILIO_PHONE_NUMBER=
|
|
372
|
-
|
|
373
|
-
# Slack
|
|
374
|
-
SLACK_WEBHOOK_URL=
|
|
375
|
-
|
|
376
|
-
# Rate Limiting
|
|
377
|
-
RATE_LIMIT_WINDOW=1m
|
|
378
|
-
RATE_LIMIT_LIMIT=100
|
|
379
|
-
|
|
380
|
-
# Logger
|
|
381
|
-
LOG_LEVEL=info
|
|
183
|
+
# AWS S3
|
|
184
|
+
AWS_REGION=us-east-1
|
|
185
|
+
AWS_ACCESS_KEY_ID=your-access-key
|
|
186
|
+
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
187
|
+
AWS_S3_BUCKET=your-bucket-name
|
|
382
188
|
```
|
|
383
189
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
```
|
|
387
|
-
saas-backend-kit/
|
|
388
|
-
├── src/
|
|
389
|
-
│ ├── auth/ # Authentication module
|
|
390
|
-
│ │ ├── types.ts
|
|
391
|
-
│ │ ├── jwt.ts
|
|
392
|
-
│ │ ├── rbac.ts
|
|
393
|
-
│ │ ├── oauth.ts
|
|
394
|
-
│ │ ├── express.ts
|
|
395
|
-
│ │ ├── fastify.ts
|
|
396
|
-
│ │ └── index.ts
|
|
397
|
-
│ ├── queue/ # Task queue (BullMQ)
|
|
398
|
-
│ ├── notifications/ # Email, SMS, Webhooks, Slack
|
|
399
|
-
│ ├── logger/ # Pino logger
|
|
400
|
-
│ ├── rate-limit/ # Rate limiting
|
|
401
|
-
│ ├── config/ # Config manager
|
|
402
|
-
│ ├── response/ # Response helpers
|
|
403
|
-
│ ├── utils/ # Utilities
|
|
404
|
-
│ ├── plugin.ts # Plugin architecture
|
|
405
|
-
│ └── index.ts # Main exports
|
|
406
|
-
├── examples/
|
|
407
|
-
│ └── express/
|
|
408
|
-
├── package.json
|
|
409
|
-
├── tsconfig.json
|
|
410
|
-
└── tsup.config.ts
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
## TypeScript
|
|
414
|
-
|
|
415
|
-
This library is written in TypeScript and provides full type definitions out of the box.
|
|
416
|
-
|
|
417
|
-
## Tree Shaking
|
|
418
|
-
|
|
419
|
-
All modules are exported separately for optimal tree shaking:
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
import { auth } from 'saas-backend-kit/auth';
|
|
423
|
-
import { queue } from 'saas-backend-kit/queue';
|
|
424
|
-
import { logger } from 'saas-backend-kit/logger';
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
## Testing
|
|
428
|
-
|
|
429
|
-
```bash
|
|
430
|
-
npm test
|
|
431
|
-
npm run test:watch
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
## Linting
|
|
435
|
-
|
|
436
|
-
```bash
|
|
437
|
-
npm run lint
|
|
438
|
-
npm run typecheck
|
|
439
|
-
```
|
|
190
|
+
---
|
|
440
191
|
|
|
441
|
-
##
|
|
192
|
+
## 🤝 Contributing
|
|
442
193
|
|
|
443
|
-
|
|
444
|
-
npm run build
|
|
445
|
-
```
|
|
194
|
+
Contributions, issues, and feature requests are welcome! Feel free to open a PR or issue.
|
|
446
195
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
MIT License - see [LICENSE](LICENSE) for details.
|
|
196
|
+
---
|
|
450
197
|
|
|
451
|
-
|
|
198
|
+
<div align="center">
|
|
452
199
|
|
|
453
|
-
|
|
200
|
+
**Built with ❤️ by [Ashish Kumar Maurya](https://github.com/AshishK-M)**
|
|
201
|
+
Senior Full-Stack Developer · Dubai, UAE
|
|
202
|
+
[](https://www.linkedin.com/in/ashish-kumar-maurya-fullstack/)
|
|
454
203
|
|
|
455
|
-
|
|
204
|
+
<sub>saas-backend-kit · MIT License · Node.js · Express · Fastify · TypeScript · BullMQ · AWS S3 · Pino · Zod</sub>
|
|
456
205
|
|
|
457
|
-
|
|
458
|
-
# saas-backend-kit
|
|
459
|
-
|
|
206
|
+
</div>
|
package/copy-dts.js
CHANGED
|
@@ -214,6 +214,64 @@ export declare class ResponseHelper {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
export declare const response: typeof ResponseHelper;
|
|
217
|
+
`,
|
|
218
|
+
'upload/index.d.ts': `export interface S3Config {
|
|
219
|
+
region?: string;
|
|
220
|
+
accessKeyId?: string;
|
|
221
|
+
secretAccessKey?: string;
|
|
222
|
+
bucket: string;
|
|
223
|
+
endpoint?: string;
|
|
224
|
+
forcePathStyle?: boolean;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface UploadOptions {
|
|
228
|
+
key?: string;
|
|
229
|
+
contentType?: string;
|
|
230
|
+
expiresIn?: number;
|
|
231
|
+
metadata?: Record<string, string>;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface UploadResult {
|
|
235
|
+
key: string;
|
|
236
|
+
url: string;
|
|
237
|
+
bucket: string;
|
|
238
|
+
contentType?: string;
|
|
239
|
+
size?: number;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface SignedUrlOptions {
|
|
243
|
+
expiresIn?: number;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface FileObject {
|
|
247
|
+
key: string;
|
|
248
|
+
lastModified?: Date;
|
|
249
|
+
size?: number;
|
|
250
|
+
contentType?: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export declare const upload: {
|
|
254
|
+
initialize: (config: S3Config) => void;
|
|
255
|
+
file: (file: Buffer | Uint8Array | string, options?: UploadOptions) => Promise<UploadResult>;
|
|
256
|
+
image: (file: Buffer | Uint8Array | string, filename: string, options?: UploadOptions) => Promise<UploadResult>;
|
|
257
|
+
video: (file: Buffer | Uint8Array | string, filename: string, options?: UploadOptions) => Promise<UploadResult>;
|
|
258
|
+
delete: (key: string) => Promise<void>;
|
|
259
|
+
getSignedUrl: (key: string, options?: SignedUrlOptions) => Promise<string>;
|
|
260
|
+
getPublicUrl: (key: string) => Promise<string>;
|
|
261
|
+
listFiles: (prefix?: string, maxKeys?: number) => Promise<FileObject[]>;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export declare class S3Service {
|
|
265
|
+
initialize(config: S3Config): void;
|
|
266
|
+
isInitialized(): boolean;
|
|
267
|
+
upload(file: Buffer | Uint8Array | string, options?: UploadOptions): Promise<UploadResult>;
|
|
268
|
+
uploadImage(file: Buffer | Uint8Array | string, filename: string, options?: UploadOptions): Promise<UploadResult>;
|
|
269
|
+
uploadVideo(file: Buffer | Uint8Array | string, filename: string, options?: UploadOptions): Promise<UploadResult>;
|
|
270
|
+
delete(key: string): Promise<void>;
|
|
271
|
+
getSignedUrl(key: string, options?: SignedUrlOptions): Promise<string>;
|
|
272
|
+
getPublicUrl(key: string): Promise<string>;
|
|
273
|
+
listFiles(prefix?: string, maxKeys?: number): Promise<FileObject[]>;
|
|
274
|
+
}
|
|
217
275
|
`,
|
|
218
276
|
'index.d.ts': `export * from './auth';
|
|
219
277
|
export * from './queue';
|
|
@@ -222,6 +280,7 @@ export * from './logger';
|
|
|
222
280
|
export * from './rate-limit';
|
|
223
281
|
export * from './config';
|
|
224
282
|
export * from './response';
|
|
283
|
+
export * from './upload';
|
|
225
284
|
|
|
226
285
|
export interface AppOptions {
|
|
227
286
|
framework?: 'express' | 'fastify';
|
package/dist/auth/index.js
CHANGED
|
@@ -130,7 +130,12 @@ var envSchema = zod.z.object({
|
|
|
130
130
|
SLACK_WEBHOOK_URL: zod.z.string().optional(),
|
|
131
131
|
RATE_LIMIT_WINDOW: zod.z.string().default("1m"),
|
|
132
132
|
RATE_LIMIT_LIMIT: zod.z.string().default("100"),
|
|
133
|
-
LOG_LEVEL: zod.z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info")
|
|
133
|
+
LOG_LEVEL: zod.z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
|
|
134
|
+
AWS_REGION: zod.z.string().default("us-east-1"),
|
|
135
|
+
AWS_ACCESS_KEY_ID: zod.z.string().optional(),
|
|
136
|
+
AWS_SECRET_ACCESS_KEY: zod.z.string().optional(),
|
|
137
|
+
AWS_S3_BUCKET: zod.z.string().optional(),
|
|
138
|
+
AWS_ENDPOINT: zod.z.string().optional()
|
|
134
139
|
});
|
|
135
140
|
var ConfigManager = class {
|
|
136
141
|
config = null;
|