saas-backend-kit 1.0.0 → 1.0.2
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 +123 -344
- package/copy-dts.js +59 -0
- package/dist/auth/index.js +7 -2
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +7 -2
- 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 +232 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +231 -42
- 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 +7 -1
- package/dist/rate-limit/index.js.map +1 -1
- package/dist/rate-limit/index.mjs +7 -1
- package/dist/rate-limit/index.mjs.map +1 -1
- package/dist/response/index.js +51 -40
- package/dist/response/index.js.map +1 -1
- package/dist/response/index.mjs +51 -40
- package/dist/response/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/jest-output.json +72 -0
- package/jest.config.js +19 -0
- package/package.json +20 -8
- package/saas-banner.svg +239 -0
- package/src/auth/jwt.ts +1 -1
- package/src/config/index.ts +5 -0
- package/src/index.ts +2 -0
- package/src/rate-limit/express.ts +1 -0
- package/src/response/index.ts +49 -40
- package/src/upload/index.ts +268 -0
- package/tests/auth.test.ts +134 -0
- package/tests/config.test.ts +36 -0
- package/tests/logger.test.ts +47 -0
- package/tests/notifications.test.ts +19 -0
- package/tests/rate-limit.test.ts +50 -0
- package/tests/upload.test.ts +33 -0
- package/tsconfig.test.json +14 -0
- package/tsup.config.ts +2 -1
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,8 @@ import { Queue, Worker } from 'bullmq';
|
|
|
4
4
|
import jwt from 'jsonwebtoken';
|
|
5
5
|
import bcrypt from 'bcryptjs';
|
|
6
6
|
import nodemailer from 'nodemailer';
|
|
7
|
-
import {
|
|
7
|
+
import { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
|
|
8
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
8
9
|
|
|
9
10
|
var __defProp = Object.defineProperty;
|
|
10
11
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -59,7 +60,12 @@ var init_config = __esm({
|
|
|
59
60
|
SLACK_WEBHOOK_URL: z.string().optional(),
|
|
60
61
|
RATE_LIMIT_WINDOW: z.string().default("1m"),
|
|
61
62
|
RATE_LIMIT_LIMIT: z.string().default("100"),
|
|
62
|
-
LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info")
|
|
63
|
+
LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
|
|
64
|
+
AWS_REGION: z.string().default("us-east-1"),
|
|
65
|
+
AWS_ACCESS_KEY_ID: z.string().optional(),
|
|
66
|
+
AWS_SECRET_ACCESS_KEY: z.string().optional(),
|
|
67
|
+
AWS_S3_BUCKET: z.string().optional(),
|
|
68
|
+
AWS_ENDPOINT: z.string().optional()
|
|
63
69
|
});
|
|
64
70
|
ConfigManager = class {
|
|
65
71
|
config = null;
|
|
@@ -431,6 +437,7 @@ var init_express = __esm({
|
|
|
431
437
|
cleanupInterval;
|
|
432
438
|
constructor() {
|
|
433
439
|
this.cleanupInterval = setInterval(() => this.cleanup(), 6e4);
|
|
440
|
+
this.cleanupInterval.unref();
|
|
434
441
|
}
|
|
435
442
|
cleanup() {
|
|
436
443
|
const now = Date.now();
|
|
@@ -561,7 +568,7 @@ var JWTService = class {
|
|
|
561
568
|
return jwt.verify(token, this.refreshSecret);
|
|
562
569
|
}
|
|
563
570
|
refreshTokens(refreshToken) {
|
|
564
|
-
const payload = this.verifyRefreshToken(refreshToken);
|
|
571
|
+
const { iat, exp, nbf, ...payload } = this.verifyRefreshToken(refreshToken);
|
|
565
572
|
return this.generateTokenPair(payload);
|
|
566
573
|
}
|
|
567
574
|
};
|
|
@@ -1038,6 +1045,8 @@ init_express();
|
|
|
1038
1045
|
|
|
1039
1046
|
// src/index.ts
|
|
1040
1047
|
init_config();
|
|
1048
|
+
|
|
1049
|
+
// src/response/index.ts
|
|
1041
1050
|
var ResponseHelper = class {
|
|
1042
1051
|
static success(res, data, message, statusCode = 200) {
|
|
1043
1052
|
const response2 = {
|
|
@@ -1104,46 +1113,226 @@ var ResponseHelper = class {
|
|
|
1104
1113
|
return res.status(204).send();
|
|
1105
1114
|
}
|
|
1106
1115
|
};
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
};
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
};
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
};
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
};
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
};
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
};
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
};
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
};
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
};
|
|
1140
|
-
|
|
1141
|
-
|
|
1116
|
+
try {
|
|
1117
|
+
const proto = __require("express").response;
|
|
1118
|
+
if (proto) {
|
|
1119
|
+
proto.success = function(data, message, statusCode = 200) {
|
|
1120
|
+
return ResponseHelper.success(this, data, message, statusCode);
|
|
1121
|
+
};
|
|
1122
|
+
proto.created = function(data, message) {
|
|
1123
|
+
return ResponseHelper.created(this, data, message);
|
|
1124
|
+
};
|
|
1125
|
+
proto.updated = function(data, message) {
|
|
1126
|
+
return ResponseHelper.updated(this, data, message);
|
|
1127
|
+
};
|
|
1128
|
+
proto.deleted = function(message) {
|
|
1129
|
+
return ResponseHelper.deleted(this, message);
|
|
1130
|
+
};
|
|
1131
|
+
proto.error = function(error, statusCode = 400, code, details) {
|
|
1132
|
+
return ResponseHelper.error(this, error, statusCode, code, details);
|
|
1133
|
+
};
|
|
1134
|
+
proto.badRequest = function(error, code) {
|
|
1135
|
+
return ResponseHelper.badRequest(this, error, code);
|
|
1136
|
+
};
|
|
1137
|
+
proto.unauthorized = function(error, code) {
|
|
1138
|
+
return ResponseHelper.unauthorized(this, error, code);
|
|
1139
|
+
};
|
|
1140
|
+
proto.forbidden = function(error, code) {
|
|
1141
|
+
return ResponseHelper.forbidden(this, error, code);
|
|
1142
|
+
};
|
|
1143
|
+
proto.notFound = function(error, code) {
|
|
1144
|
+
return ResponseHelper.notFound(this, error, code);
|
|
1145
|
+
};
|
|
1146
|
+
proto.conflict = function(error, code) {
|
|
1147
|
+
return ResponseHelper.conflict(this, error, code);
|
|
1148
|
+
};
|
|
1149
|
+
proto.validationError = function(error, details) {
|
|
1150
|
+
return ResponseHelper.validationError(this, error, details);
|
|
1151
|
+
};
|
|
1152
|
+
proto.internalError = function(error) {
|
|
1153
|
+
return ResponseHelper.internalError(this, error);
|
|
1154
|
+
};
|
|
1155
|
+
proto.paginated = function(data, page, limit, total) {
|
|
1156
|
+
return ResponseHelper.paginated(this, data, page, limit, total);
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
} catch {
|
|
1160
|
+
}
|
|
1161
|
+
var response = ResponseHelper;
|
|
1162
|
+
|
|
1163
|
+
// src/upload/index.ts
|
|
1164
|
+
init_config();
|
|
1165
|
+
init_logger();
|
|
1166
|
+
var S3Service = class {
|
|
1167
|
+
client = null;
|
|
1168
|
+
bucket;
|
|
1169
|
+
initialized = false;
|
|
1170
|
+
constructor() {
|
|
1171
|
+
this.bucket = "";
|
|
1172
|
+
}
|
|
1173
|
+
initialize(config2) {
|
|
1174
|
+
this.client = new S3Client({
|
|
1175
|
+
region: config2.region || "us-east-1",
|
|
1176
|
+
credentials: config2.accessKeyId && config2.secretAccessKey ? {
|
|
1177
|
+
accessKeyId: config2.accessKeyId,
|
|
1178
|
+
secretAccessKey: config2.secretAccessKey
|
|
1179
|
+
} : void 0,
|
|
1180
|
+
endpoint: config2.endpoint,
|
|
1181
|
+
forcePathStyle: config2.forcePathStyle || false
|
|
1182
|
+
});
|
|
1183
|
+
this.bucket = config2.bucket;
|
|
1184
|
+
this.initialized = true;
|
|
1185
|
+
logger.info("S3 service initialized", { bucket: this.bucket });
|
|
1186
|
+
}
|
|
1187
|
+
isInitialized() {
|
|
1188
|
+
return this.initialized;
|
|
1189
|
+
}
|
|
1190
|
+
ensureInitialized() {
|
|
1191
|
+
if (!this.initialized) {
|
|
1192
|
+
const region = config.get("AWS_REGION") || "us-east-1";
|
|
1193
|
+
const bucket = config.get("AWS_S3_BUCKET") || "";
|
|
1194
|
+
this.initialize({
|
|
1195
|
+
region,
|
|
1196
|
+
accessKeyId: config.get("AWS_ACCESS_KEY_ID"),
|
|
1197
|
+
secretAccessKey: config.get("AWS_SECRET_ACCESS_KEY"),
|
|
1198
|
+
bucket,
|
|
1199
|
+
endpoint: config.get("AWS_ENDPOINT")
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
async upload(file, options = {}) {
|
|
1204
|
+
this.ensureInitialized();
|
|
1205
|
+
const key = options.key || this.generateKey();
|
|
1206
|
+
const contentType = options.contentType || this.guessContentType(key);
|
|
1207
|
+
const command = new PutObjectCommand({
|
|
1208
|
+
Bucket: this.bucket,
|
|
1209
|
+
Key: key,
|
|
1210
|
+
Body: file,
|
|
1211
|
+
ContentType: contentType,
|
|
1212
|
+
Metadata: options.metadata
|
|
1213
|
+
});
|
|
1214
|
+
await this.client.send(command);
|
|
1215
|
+
const url = await this.getSignedUrl(key, { expiresIn: options.expiresIn || 3600 });
|
|
1216
|
+
logger.info("File uploaded to S3", { key, bucket: this.bucket, contentType });
|
|
1217
|
+
return {
|
|
1218
|
+
key,
|
|
1219
|
+
url,
|
|
1220
|
+
bucket: this.bucket,
|
|
1221
|
+
contentType
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
async uploadImage(file, filename, options = {}) {
|
|
1225
|
+
const key = options.key || `images/${Date.now()}-${filename}`;
|
|
1226
|
+
return this.upload(file, {
|
|
1227
|
+
...options,
|
|
1228
|
+
key,
|
|
1229
|
+
contentType: options.contentType || this.getImageContentType(filename)
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
async uploadVideo(file, filename, options = {}) {
|
|
1233
|
+
const key = options.key || `videos/${Date.now()}-${filename}`;
|
|
1234
|
+
return this.upload(file, {
|
|
1235
|
+
...options,
|
|
1236
|
+
key,
|
|
1237
|
+
contentType: options.contentType || this.getVideoContentType(filename)
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
async delete(key) {
|
|
1241
|
+
this.ensureInitialized();
|
|
1242
|
+
const command = new DeleteObjectCommand({
|
|
1243
|
+
Bucket: this.bucket,
|
|
1244
|
+
Key: key
|
|
1245
|
+
});
|
|
1246
|
+
await this.client.send(command);
|
|
1247
|
+
logger.info("File deleted from S3", { key, bucket: this.bucket });
|
|
1248
|
+
}
|
|
1249
|
+
async getSignedUrl(key, options = {}) {
|
|
1250
|
+
this.ensureInitialized();
|
|
1251
|
+
const command = new GetObjectCommand({
|
|
1252
|
+
Bucket: this.bucket,
|
|
1253
|
+
Key: key
|
|
1254
|
+
});
|
|
1255
|
+
return getSignedUrl(this.client, command, {
|
|
1256
|
+
expiresIn: options.expiresIn || 3600
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
async getPublicUrl(key) {
|
|
1260
|
+
return `https://${this.bucket}.s3.${config.get("AWS_REGION") || "us-east-1"}.amazonaws.com/${key}`;
|
|
1261
|
+
}
|
|
1262
|
+
async listFiles(prefix, maxKeys = 1e3) {
|
|
1263
|
+
this.ensureInitialized();
|
|
1264
|
+
const command = new ListObjectsV2Command({
|
|
1265
|
+
Bucket: this.bucket,
|
|
1266
|
+
Prefix: prefix,
|
|
1267
|
+
MaxKeys: maxKeys
|
|
1268
|
+
});
|
|
1269
|
+
const response2 = await this.client.send(command);
|
|
1270
|
+
return (response2.Contents || []).map((item) => ({
|
|
1271
|
+
key: item.Key || "",
|
|
1272
|
+
lastModified: item.LastModified,
|
|
1273
|
+
size: item.Size
|
|
1274
|
+
}));
|
|
1275
|
+
}
|
|
1276
|
+
generateKey() {
|
|
1277
|
+
const timestamp = Date.now();
|
|
1278
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
1279
|
+
return `uploads/${timestamp}-${random}`;
|
|
1280
|
+
}
|
|
1281
|
+
guessContentType(key) {
|
|
1282
|
+
const ext = key.split(".").pop()?.toLowerCase();
|
|
1283
|
+
const contentTypes = {
|
|
1284
|
+
jpg: "image/jpeg",
|
|
1285
|
+
jpeg: "image/jpeg",
|
|
1286
|
+
png: "image/png",
|
|
1287
|
+
gif: "image/gif",
|
|
1288
|
+
webp: "image/webp",
|
|
1289
|
+
svg: "image/svg+xml",
|
|
1290
|
+
mp4: "video/mp4",
|
|
1291
|
+
webm: "video/webm",
|
|
1292
|
+
mov: "video/quicktime",
|
|
1293
|
+
avi: "video/x-msvideo",
|
|
1294
|
+
pdf: "application/pdf",
|
|
1295
|
+
json: "application/json",
|
|
1296
|
+
txt: "text/plain"
|
|
1297
|
+
};
|
|
1298
|
+
return contentTypes[ext || ""] || "application/octet-stream";
|
|
1299
|
+
}
|
|
1300
|
+
getImageContentType(filename) {
|
|
1301
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
1302
|
+
const imageTypes = {
|
|
1303
|
+
jpg: "image/jpeg",
|
|
1304
|
+
jpeg: "image/jpeg",
|
|
1305
|
+
png: "image/png",
|
|
1306
|
+
gif: "image/gif",
|
|
1307
|
+
webp: "image/webp",
|
|
1308
|
+
svg: "image/svg+xml"
|
|
1309
|
+
};
|
|
1310
|
+
return imageTypes[ext || ""] || "image/jpeg";
|
|
1311
|
+
}
|
|
1312
|
+
getVideoContentType(filename) {
|
|
1313
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
1314
|
+
const videoTypes = {
|
|
1315
|
+
mp4: "video/mp4",
|
|
1316
|
+
webm: "video/webm",
|
|
1317
|
+
mov: "video/quicktime",
|
|
1318
|
+
avi: "video/x-msvideo",
|
|
1319
|
+
mkv: "video/x-matroska",
|
|
1320
|
+
ogv: "video/ogg"
|
|
1321
|
+
};
|
|
1322
|
+
return videoTypes[ext || ""] || "video/mp4";
|
|
1323
|
+
}
|
|
1142
1324
|
};
|
|
1143
|
-
|
|
1144
|
-
|
|
1325
|
+
var s3Service = new S3Service();
|
|
1326
|
+
var upload = {
|
|
1327
|
+
initialize: (config2) => s3Service.initialize(config2),
|
|
1328
|
+
file: (file, options) => s3Service.upload(file, options),
|
|
1329
|
+
image: (file, filename, options) => s3Service.uploadImage(file, filename, options),
|
|
1330
|
+
video: (file, filename, options) => s3Service.uploadVideo(file, filename, options),
|
|
1331
|
+
delete: (key) => s3Service.delete(key),
|
|
1332
|
+
getSignedUrl: (key, options) => s3Service.getSignedUrl(key, options),
|
|
1333
|
+
getPublicUrl: (key) => s3Service.getPublicUrl(key),
|
|
1334
|
+
listFiles: (prefix, maxKeys) => s3Service.listFiles(prefix, maxKeys)
|
|
1145
1335
|
};
|
|
1146
|
-
var response = ResponseHelper;
|
|
1147
1336
|
|
|
1148
1337
|
// src/plugin.ts
|
|
1149
1338
|
init_logger();
|
|
@@ -1276,6 +1465,6 @@ init_queue();
|
|
|
1276
1465
|
init_logger();
|
|
1277
1466
|
init_config();
|
|
1278
1467
|
|
|
1279
|
-
export { Auth, AuthService, PluginManager, QueueManager, ResponseHelper, SaaSAppBuilder, auth, config, createApp, createAuth, createExpressApp, createQueue, createRateLimiter, logger, notification, notify, queue, rateLimit, response };
|
|
1468
|
+
export { Auth, AuthService, PluginManager, QueueManager, ResponseHelper, SaaSAppBuilder, auth, config, createApp, createAuth, createExpressApp, createQueue, createRateLimiter, logger, notification, notify, queue, rateLimit, response, s3Service, upload };
|
|
1280
1469
|
//# sourceMappingURL=index.mjs.map
|
|
1281
1470
|
//# sourceMappingURL=index.mjs.map
|