powr-sdk-api 1.0.0 → 1.1.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.
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ const jwt = require('jsonwebtoken');
4
+ // const User = require('../models/user');
5
+
6
+ exports.protect = async (req, res, next) => {
7
+ try {
8
+ let token;
9
+
10
+ // Check if token exists in headers
11
+ if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
12
+ token = req.headers.authorization.split(' ')[1];
13
+ }
14
+ if (!token) {
15
+ return res.error('Not authorized to access this route', 401);
16
+ }
17
+ try {
18
+ // Verify token
19
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
20
+
21
+ // // Get user from token
22
+ // const user = await User.findByPk(decoded.userId);
23
+ // if (!user) {
24
+ // return res.error('User not found', 401);
25
+ // }
26
+
27
+ // // Add user and userId to request object
28
+ // req.user = user;
29
+ req.userId = decoded.userId;
30
+ next();
31
+ } catch (error) {
32
+ return res.error('Not authorized to access this route', 401, error);
33
+ }
34
+ } catch (error) {
35
+ return res.error('Error authenticating user', 500, error);
36
+ }
37
+ };
38
+
39
+ // exports.authorize = (...roles) => {
40
+ // return (req, res, next) => {
41
+ // if (!roles.includes(req.user.role)) {
42
+ // return res.error(`User role ${req.user.role} is not authorized to access this route`, 403);
43
+ // }
44
+ // next();
45
+ // };
46
+ // };
@@ -1,36 +1,19 @@
1
1
  "use strict";
2
2
 
3
- /**
4
- * Error handling middleware for Express applications
5
- */
6
-
7
- class APIError extends Error {
8
- constructor(message, statusCode = 500, details = null) {
9
- super(message);
10
- this.statusCode = statusCode;
11
- this.details = details;
12
- this.name = 'APIError';
13
- }
14
- }
15
- const asyncHandler = fn => (req, res, next) => {
3
+ const logger = require("../services/logger");
4
+ const errorCatcher = fn => (req, res, next) => {
16
5
  Promise.resolve(fn(req, res, next)).catch(next);
17
6
  };
18
7
  const errorHandler = (err, req, res, next) => {
19
- console.error('Error:', {
20
- message: err.message,
8
+ logger.error("Error occurred", {
9
+ error: err.message,
21
10
  stack: err.stack,
22
- details: err.details
23
- });
24
- const statusCode = err.statusCode || 500;
25
- const message = err.message || 'Internal Server Error';
26
- res.status(statusCode).json({
27
- success: false,
28
- message,
29
- details: err.details
11
+ path: req.path,
12
+ method: req.method
30
13
  });
14
+ return res.error(err.message, err.statusCode, err.details);
31
15
  };
32
16
  module.exports = {
33
- APIError,
34
- asyncHandler,
17
+ errorCatcher,
35
18
  errorHandler
36
19
  };
@@ -5,40 +5,41 @@
5
5
  */
6
6
 
7
7
  const logger = (req, res, next) => {
8
- // Log request
9
- console.log('Request:', {
10
- timestamp: new Date().toISOString(),
11
- method: req.method,
12
- url: req.url,
13
- ip: req.ip,
14
- headers: req.headers,
15
- body: req.body,
16
- query: req.query
17
- });
18
-
19
- // Store original send and json methods
20
- const originalSend = res.send;
21
- const originalJson = res.json;
8
+ const startTime = Date.now();
22
9
 
23
- // Override send method to log response
24
- res.send = function (body) {
25
- console.log('Response:', {
10
+ // Log request with redacted sensitive information
11
+ try {
12
+ const logBody = {
13
+ ...req.body
14
+ };
15
+ if (logBody.password) logBody.password = "[REDACTED]";
16
+ if (logBody.token) logBody.token = "[REDACTED]";
17
+ console.log('Request:', {
26
18
  timestamp: new Date().toISOString(),
27
- statusCode: res.statusCode,
28
- body: body
19
+ method: req.method,
20
+ url: req.originalUrl,
21
+ ip: req.ip,
22
+ userAgent: req.get('user-agent'),
23
+ body: logBody,
24
+ query: req.query
29
25
  });
30
- return originalSend.call(this, body);
31
- };
26
+ } catch (error) {
27
+ console.error("Error logging request:", error);
28
+ }
32
29
 
33
- // Override json method to log response
34
- res.json = function (body) {
30
+ // Log response with timing information
31
+ res.on('finish', () => {
32
+ const responseTime = Date.now() - startTime;
35
33
  console.log('Response:', {
36
34
  timestamp: new Date().toISOString(),
35
+ method: req.method,
36
+ url: req.originalUrl,
37
37
  statusCode: res.statusCode,
38
- body: body
38
+ responseTime: `${responseTime}ms`,
39
+ userAgent: req.get('user-agent'),
40
+ ip: req.ip
39
41
  });
40
- return originalJson.call(this, body);
41
- };
42
+ });
42
43
  next();
43
44
  };
44
45
  module.exports = logger;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ const logger = require("../services/logger");
4
+ const requestLogger = (req, res, next) => {
5
+ try {
6
+ // Only log the request body, redacting sensitive information
7
+ const logBody = {
8
+ ...req.body
9
+ };
10
+ if (logBody.password) logBody.password = "[REDACTED]";
11
+ if (logBody.token) logBody.token = "[REDACTED]";
12
+ logger.info(`${req.method} ${req.path}`, logBody);
13
+ next();
14
+ } catch (error) {
15
+ // If logging fails, still continue with the request
16
+ console.error("Error logging request:", error);
17
+ next();
18
+ }
19
+ };
20
+ module.exports = {
21
+ requestLogger
22
+ };
@@ -1,44 +1,53 @@
1
1
  "use strict";
2
2
 
3
- /**
4
- * Response formatting middleware for Express applications
5
- */
6
-
7
- const responseHandler = (req, res, next) => {
3
+ const logger = require('../services/logger');
4
+ const responseFormatter = (req, res, next) => {
8
5
  // Add success method to response object
9
- res.success = function (data = null, message = 'Success', statusCode = 200) {
10
- return this.status(statusCode).json({
6
+ res.success = function (message = 'Success', statusCode = 200, data = null) {
7
+ const response = {
11
8
  success: true,
12
- message,
13
- data
14
- });
9
+ message
10
+ };
11
+
12
+ // Only include data if it's not null
13
+ if (data !== null) {
14
+ response.data = data;
15
+ }
16
+ return this.status(statusCode).json(response);
15
17
  };
16
18
 
17
19
  // Add error method to response object
18
- res.error = function (message = 'Error', statusCode = 500, details = null) {
19
- return this.status(statusCode).json({
20
+ res.error = function (message = 'Error', statusCode = 500, error = null) {
21
+ const response = {
20
22
  success: false,
21
- message,
22
- details
23
- });
24
- };
25
-
26
- // Store original json method
27
- const originalJson = res.json;
23
+ message
24
+ };
28
25
 
29
- // Override json method to ensure consistent format
30
- res.json = function (data) {
31
- // If response is already formatted, use original json
32
- if (data && typeof data === 'object' && 'success' in data) {
33
- return originalJson.call(this, data);
26
+ // Only include details if they're not null
27
+ if (error !== null) {
28
+ response.error = error;
34
29
  }
35
-
36
- // Format the response
37
- return originalJson.call(this, {
38
- success: true,
39
- data
40
- });
30
+ return this.status(statusCode).json(response);
41
31
  };
42
32
  next();
43
33
  };
44
- module.exports = responseHandler;
34
+ const responseLogger = (req, res, next) => {
35
+ const startTime = Date.now();
36
+ res.on('finish', () => {
37
+ const responseTime = Date.now() - startTime;
38
+ logger.info('API Response', {
39
+ method: req.method,
40
+ url: req.originalUrl,
41
+ statusCode: res.statusCode,
42
+ responseTime: `${responseTime}ms`,
43
+ userAgent: req.get('user-agent'),
44
+ ip: req.ip,
45
+ response: res.locals.responseBody || '[not captured]'
46
+ });
47
+ });
48
+ next();
49
+ };
50
+ module.exports = {
51
+ responseFormatter,
52
+ responseLogger
53
+ };
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ const winston = require('winston');
4
+ const {
5
+ S3Transport
6
+ } = require('../utils/s3');
7
+
8
+ // Custom format for logs
9
+ const logFormat = winston.format.combine(winston.format.timestamp(), winston.format.errors({
10
+ stack: true
11
+ }), winston.format.json());
12
+
13
+ // Create base transports array with console transport
14
+ const transports = [new winston.transports.Console()];
15
+
16
+ // Add S3 transport only in production
17
+ if (process.env.NODE_ENV === 'production') {
18
+ transports.push(new S3Transport({
19
+ bucket: process.env.AWS_LOG_BUCKET_NAME,
20
+ prefix: 'logs',
21
+ bufferSize: 100,
22
+ flushInterval: 5000
23
+ }));
24
+ }
25
+
26
+ // Create the logger
27
+ const logger = winston.createLogger({
28
+ level: process.env.LOG_LEVEL || 'info',
29
+ format: logFormat,
30
+ defaultMeta: {
31
+ service: 'justhurt-api'
32
+ },
33
+ transports
34
+ });
35
+ module.exports = logger;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ const jwt = require('jsonwebtoken');
4
+
5
+ /**
6
+ * Generates a JWT token for a user
7
+ * @param {Object} user - The user object
8
+ * @returns {string} - The generated JWT token
9
+ */
10
+ const generateToken = user => {
11
+ return jwt.sign({
12
+ userId: user.userId
13
+ }, process.env.JWT_SECRET, {
14
+ expiresIn: process.env.JWT_EXPIRES_IN || '24h'
15
+ });
16
+ };
17
+ module.exports = {
18
+ generateToken
19
+ };
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Logger utility for Express applications
5
+ */
6
+
7
+ const winston = require('winston');
8
+ const S3Transport = require('./s3-transport');
9
+
10
+ /**
11
+ * Creates a Winston logger instance with configured transports
12
+ * @param {Object} options - Logger configuration options
13
+ * @param {string} options.service - Service name for logging
14
+ * @param {string} options.level - Log level (default: 'info')
15
+ * @param {Object} options.s3 - S3 transport configuration
16
+ * @param {string} options.s3.bucket - S3 bucket name
17
+ * @param {string} options.s3.prefix - S3 key prefix
18
+ * @param {number} options.s3.bufferSize - Buffer size for S3 uploads
19
+ * @param {number} options.s3.flushInterval - Flush interval in milliseconds
20
+ * @returns {winston.Logger} Configured Winston logger instance
21
+ */
22
+ const createLogger = (options = {}) => {
23
+ const {
24
+ service = 'api',
25
+ level = process.env.LOG_LEVEL || 'info',
26
+ s3 = null
27
+ } = options;
28
+
29
+ // Custom format for logs
30
+ const logFormat = winston.format.combine(winston.format.timestamp(), winston.format.errors({
31
+ stack: true
32
+ }), winston.format.json());
33
+
34
+ // Create base transports array with console transport
35
+ const transports = [new winston.transports.Console()];
36
+
37
+ // Add S3 transport if configured
38
+ if (s3 && process.env.NODE_ENV === 'production') {
39
+ transports.push(new S3Transport({
40
+ bucket: s3.bucket,
41
+ prefix: s3.prefix || 'logs',
42
+ bufferSize: s3.bufferSize || 100,
43
+ flushInterval: s3.flushInterval || 5000
44
+ }));
45
+ }
46
+
47
+ // Create and return the logger
48
+ return winston.createLogger({
49
+ level,
50
+ format: logFormat,
51
+ defaultMeta: {
52
+ service
53
+ },
54
+ transports
55
+ });
56
+ };
57
+ module.exports = createLogger;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * S3 Transport for Winston logger
5
+ */
6
+
7
+ const winston = require('winston');
8
+ const {
9
+ S3Client,
10
+ PutObjectCommand
11
+ } = require('@aws-sdk/client-s3');
12
+
13
+ /**
14
+ * Custom Winston transport for logging to S3
15
+ */
16
+ class S3Transport extends winston.Transport {
17
+ constructor(opts) {
18
+ super(opts);
19
+ this.bucket = opts.bucket;
20
+ this.prefix = opts.prefix || '';
21
+ this.buffer = [];
22
+ this.bufferSize = opts.bufferSize || 100;
23
+ this.flushInterval = opts.flushInterval || 5000;
24
+ this.setupFlushInterval();
25
+ }
26
+ setupFlushInterval() {
27
+ setInterval(() => {
28
+ this.flush();
29
+ }, this.flushInterval);
30
+ }
31
+ async flush() {
32
+ if (this.buffer.length === 0) return;
33
+ const logs = this.buffer.splice(0, this.buffer.length);
34
+ const date = new Date().toISOString().split('T')[0];
35
+ const key = `${this.prefix}/${date}/${Date.now()}.json`;
36
+ try {
37
+ const command = new PutObjectCommand({
38
+ Bucket: this.bucket,
39
+ Key: key,
40
+ Body: JSON.stringify(logs),
41
+ ContentType: 'application/json'
42
+ });
43
+ await this.s3Client.send(command);
44
+ } catch (error) {
45
+ console.error('Error flushing logs to S3:', error);
46
+ // Put logs back in buffer
47
+ this.buffer.unshift(...logs);
48
+ }
49
+ }
50
+ log(info, callback) {
51
+ setImmediate(() => {
52
+ this.emit('logged', info);
53
+ });
54
+ this.buffer.push(info);
55
+ if (this.buffer.length >= this.bufferSize) {
56
+ this.flush();
57
+ }
58
+ callback();
59
+ }
60
+ }
61
+ module.exports = S3Transport;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ const winston = require('winston');
4
+ const {
5
+ S3Client,
6
+ PutObjectCommand
7
+ } = require('@aws-sdk/client-s3');
8
+
9
+ // Create S3 client
10
+ const s3Client = new S3Client({
11
+ region: process.env.AWS_REGION,
12
+ credentials: {
13
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
14
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
15
+ }
16
+ });
17
+
18
+ /**
19
+ * Custom Winston transport for logging to S3
20
+ */
21
+ class S3Transport extends winston.Transport {
22
+ constructor(opts) {
23
+ super(opts);
24
+ this.bucket = opts.bucket;
25
+ this.prefix = opts.prefix || '';
26
+ this.buffer = [];
27
+ this.bufferSize = opts.bufferSize || 100;
28
+ this.flushInterval = opts.flushInterval || 5000;
29
+ this.setupFlushInterval();
30
+ }
31
+ setupFlushInterval() {
32
+ setInterval(() => {
33
+ this.flush();
34
+ }, this.flushInterval);
35
+ }
36
+ async flush() {
37
+ if (this.buffer.length === 0) return;
38
+ const logs = this.buffer.splice(0, this.buffer.length);
39
+ const date = new Date().toISOString().split('T')[0];
40
+ const key = `${this.prefix}/${date}/${Date.now()}.json`;
41
+ try {
42
+ const command = new PutObjectCommand({
43
+ Bucket: this.bucket,
44
+ Key: key,
45
+ Body: JSON.stringify(logs),
46
+ ContentType: 'application/json'
47
+ });
48
+ await s3Client.send(command);
49
+ } catch (error) {
50
+ console.error('Failed to write logs to S3:', {
51
+ error: error.message,
52
+ code: error.code,
53
+ bucket: this.bucket,
54
+ key: key,
55
+ region: process.env.AWS_REGION,
56
+ stack: error.stack
57
+ });
58
+ // Put the logs back in the buffer
59
+ this.buffer.unshift(...logs);
60
+ }
61
+ }
62
+ log(info, callback) {
63
+ setImmediate(() => {
64
+ this.emit('logged', info);
65
+ });
66
+ this.buffer.push({
67
+ timestamp: new Date().toISOString(),
68
+ ...info
69
+ });
70
+ if (this.buffer.length >= this.bufferSize) {
71
+ this.flush();
72
+ }
73
+ callback();
74
+ }
75
+ }
76
+ module.exports = {
77
+ S3Transport
78
+ };
package/package.json CHANGED
@@ -1,60 +1,60 @@
1
- {
2
- "name": "powr-sdk-api",
3
- "version": "1.0.0",
4
- "description": "Shared API core library for PowrStack projects",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "files": [
8
- "dist",
9
- "README.md"
10
- ],
11
- "scripts": {
12
- "test": "jest --passWithNoTests",
13
- "lint": "eslint .",
14
- "build": "babel src -d dist",
15
- "prepare": "npm run build",
16
- "prepublishOnly": "npm run test"
17
- },
18
- "keywords": [
19
- "api",
20
- "express",
21
- "middleware",
22
- "error-handling",
23
- "response-formatting",
24
- "logging",
25
- "swagger",
26
- "documentation"
27
- ],
28
- "author": "PowrStack",
29
- "license": "ISC",
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/powrstack/powr-sdk-api.git"
33
- },
34
- "bugs": {
35
- "url": "https://github.com/powrstack/powr-sdk-api/issues"
36
- },
37
- "homepage": "https://github.com/powrstack/powr-sdk-api#readme",
38
- "dependencies": {
39
- "express": "^4.18.2",
40
- "swagger-jsdoc": "^6.2.8",
41
- "swagger-ui-express": "^5.0.0"
42
- },
43
- "devDependencies": {
44
- "@babel/cli": "^7.23.9",
45
- "@babel/core": "^7.24.0",
46
- "@babel/preset-env": "^7.24.0",
47
- "@types/express": "^4.17.21",
48
- "@types/swagger-jsdoc": "^6.0.4",
49
- "@types/swagger-ui-express": "^4.1.6",
50
- "eslint": "^8.57.0",
51
- "jest": "^29.7.0",
52
- "typescript": "^5.3.3"
53
- },
54
- "peerDependencies": {
55
- "express": "^4.18.2"
56
- },
57
- "engines": {
58
- "node": ">=14.0.0"
59
- }
60
- }
1
+ {
2
+ "name": "powr-sdk-api",
3
+ "version": "1.1.1",
4
+ "description": "Shared API core library for PowrStack projects",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "test": "jest --passWithNoTests",
13
+ "lint": "eslint .",
14
+ "build": "babel src -d dist",
15
+ "prepare": "npm run build",
16
+ "prepublishOnly": "npm run test"
17
+ },
18
+ "keywords": [
19
+ "api",
20
+ "express",
21
+ "middleware",
22
+ "error-handling",
23
+ "response-formatting",
24
+ "logging",
25
+ "swagger",
26
+ "documentation"
27
+ ],
28
+ "author": "PowrStack",
29
+ "license": "ISC",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/powrstack/powr-sdk-api.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/powrstack/powr-sdk-api/issues"
36
+ },
37
+ "homepage": "https://github.com/powrstack/powr-sdk-api#readme",
38
+ "dependencies": {
39
+ "express": "^4.18.2",
40
+ "swagger-jsdoc": "^6.2.8",
41
+ "swagger-ui-express": "^5.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@babel/cli": "^7.23.9",
45
+ "@babel/core": "^7.24.0",
46
+ "@babel/preset-env": "^7.24.0",
47
+ "@types/express": "^4.17.21",
48
+ "@types/swagger-jsdoc": "^6.0.4",
49
+ "@types/swagger-ui-express": "^4.1.6",
50
+ "eslint": "^8.57.0",
51
+ "jest": "^29.7.0",
52
+ "typescript": "^5.3.3"
53
+ },
54
+ "peerDependencies": {
55
+ "express": "^4.18.2"
56
+ },
57
+ "engines": {
58
+ "node": ">=14.0.0"
59
+ }
60
+ }