@triophore/falcon-cli 1.0.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 +62 -0
- package/auth/basic.js +8 -0
- package/auth/cookie.js +10 -0
- package/auth/jwks.js +6 -0
- package/auth/jwt.js +9 -0
- package/auth/openid.js +5 -0
- package/auth/webscoket.js +6 -0
- package/builder/EnvBuilder.js +0 -0
- package/builder/createCjsModule.js +95 -0
- package/builder/editModelInteractive.js +159 -0
- package/builder/interactiveModelBuilder.js +215 -0
- package/builder/interactiveMongobuilder.js +189 -0
- package/builder/interactiveUnifiedBuilder.js +277 -0
- package/builder/joiValidatorBuilder.js +218 -0
- package/builder/mongooseModelBuilder.js +290 -0
- package/builder/mongooseModelBuilder2.js +313 -0
- package/builder/runMigrations.js +106 -0
- package/builder/sequelizeModelBuilder.js +180 -0
- package/cli.js +60 -0
- package/commands/create.js +57 -0
- package/commands/generate.js +74 -0
- package/dev/Uset.schema.json +18 -0
- package/dev/buildSchemaInteractive.js +189 -0
- package/dev/buildSequelizeSchemaInteractive.js +128 -0
- package/dev/createJoiSchemaFromJson.js +137 -0
- package/dev/createModelFromJson.js +280 -0
- package/dev/generateAllFiles.js +45 -0
- package/dev/generateJoiFile.js +95 -0
- package/dev/generateSequelizeFiles.js +167 -0
- package/dev/interactiveJoiBuilder.js +177 -0
- package/dev/ra.js +22 -0
- package/dev/rj.js +18 -0
- package/dev/run.js +16 -0
- package/dev/run_seq.js +18 -0
- package/dev/tracker.js +23 -0
- package/editJsConfig.js +188 -0
- package/index.js +548 -0
- package/lib/ModelGenerator.js +203 -0
- package/lib/ProjectGenerator.js +246 -0
- package/lib/utils.js +100 -0
- package/logo.js +3 -0
- package/package.json +35 -0
- package/readme.md +2 -0
- package/schema.json +42 -0
- package/templates/auth_vals.json +3 -0
- package/templates/config.js +0 -0
- package/templates/example-route.js +94 -0
- package/templates/example-service.js +63 -0
- package/templates/example-validator.js +15 -0
- package/templates/example-worker.js +83 -0
- package/templates/index.txt +41 -0
- package/templates/post-init.js +78 -0
- package/templates/settings.js +192 -0
- package/templates/template1.settings.txt +15 -0
- package/templates/templatev1.json +38 -0
- package/validateJsConfig.js +125 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example route handler
|
|
3
|
+
* This file demonstrates the route structure expected by Falcon.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports.route = async function (server, context) {
|
|
7
|
+
|
|
8
|
+
// Example GET route
|
|
9
|
+
server.route({
|
|
10
|
+
method: 'GET',
|
|
11
|
+
path: '/api/example',
|
|
12
|
+
options: {
|
|
13
|
+
tags: ['api', 'example'],
|
|
14
|
+
description: 'Example GET endpoint',
|
|
15
|
+
notes: 'Returns example data'
|
|
16
|
+
},
|
|
17
|
+
handler: async (request, h) => {
|
|
18
|
+
return {
|
|
19
|
+
success: true,
|
|
20
|
+
message: 'Hello from Falcon.js!',
|
|
21
|
+
timestamp: new Date().toISOString()
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Example POST route with validation
|
|
27
|
+
server.route({
|
|
28
|
+
method: 'POST',
|
|
29
|
+
path: '/api/example',
|
|
30
|
+
options: {
|
|
31
|
+
tags: ['api', 'example'],
|
|
32
|
+
description: 'Example POST endpoint',
|
|
33
|
+
validate: {
|
|
34
|
+
payload: context.validators?.ExamplePayload || undefined
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
handler: async (request, h) => {
|
|
38
|
+
const { name, email } = request.payload;
|
|
39
|
+
|
|
40
|
+
// Example: Save to database if models are available
|
|
41
|
+
if (context.models.example) {
|
|
42
|
+
const result = await context.models.example.create({
|
|
43
|
+
name,
|
|
44
|
+
email,
|
|
45
|
+
createdAt: new Date()
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
data: result,
|
|
51
|
+
message: 'Data saved successfully'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
message: 'Data received',
|
|
58
|
+
data: { name, email }
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Example route that sends job to worker
|
|
64
|
+
server.route({
|
|
65
|
+
method: 'POST',
|
|
66
|
+
path: '/api/example/job',
|
|
67
|
+
options: {
|
|
68
|
+
tags: ['api', 'jobs'],
|
|
69
|
+
description: 'Queue a job for processing'
|
|
70
|
+
},
|
|
71
|
+
handler: async (request, h) => {
|
|
72
|
+
const jobData = request.payload;
|
|
73
|
+
|
|
74
|
+
// Send job to worker via MQTT (if available)
|
|
75
|
+
if (context.mqtt_client) {
|
|
76
|
+
context.mqtt_client.publish('worker_example_job', JSON.stringify({
|
|
77
|
+
jobId: Date.now().toString(),
|
|
78
|
+
data: jobData,
|
|
79
|
+
timestamp: new Date().toISOString()
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
success: true,
|
|
84
|
+
message: 'Job queued for processing'
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
message: 'Job queue not available'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { FalconBaseService } = require('falconjs');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example service - rename and customize as needed
|
|
5
|
+
*/
|
|
6
|
+
class ExampleService extends FalconBaseService {
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
super('example'); // Service ID for MQTT topics
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Handle incoming MQTT messages
|
|
14
|
+
*/
|
|
15
|
+
async onMessage(topic, msg) {
|
|
16
|
+
try {
|
|
17
|
+
const message = JSON.parse(msg.toString());
|
|
18
|
+
|
|
19
|
+
switch (topic) {
|
|
20
|
+
case 'service_example':
|
|
21
|
+
await this.handleRequest(message);
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
console.log(`Unhandled topic: ${topic}`);
|
|
25
|
+
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error processing message:', error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Handle service requests
|
|
33
|
+
*/
|
|
34
|
+
async handleRequest(request) {
|
|
35
|
+
console.log('Processing request:', request);
|
|
36
|
+
|
|
37
|
+
// Your service logic here
|
|
38
|
+
|
|
39
|
+
// Send response back via MQTT
|
|
40
|
+
this.publish({
|
|
41
|
+
status: 'completed',
|
|
42
|
+
result: 'success',
|
|
43
|
+
timestamp: new Date().toISOString()
|
|
44
|
+
}, 'service_example_response');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Service main logic
|
|
49
|
+
*/
|
|
50
|
+
async run() {
|
|
51
|
+
console.log('Example service is running...');
|
|
52
|
+
|
|
53
|
+
// Your initialization logic here
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Start the service if this file is run directly
|
|
58
|
+
if (require.main === module) {
|
|
59
|
+
const service = new ExampleService();
|
|
60
|
+
service.init().catch(console.error);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = ExampleService;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const Joi = require('joi');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example Joi validator for API endpoints
|
|
5
|
+
* This validator can be automatically matched with routes by name
|
|
6
|
+
*/
|
|
7
|
+
module.exports = Joi.object({
|
|
8
|
+
name: Joi.string().min(2).max(50).required().description('User name'),
|
|
9
|
+
email: Joi.string().email().required().description('User email address'),
|
|
10
|
+
age: Joi.number().integer().min(18).max(120).optional().description('User age'),
|
|
11
|
+
preferences: Joi.object({
|
|
12
|
+
newsletter: Joi.boolean().default(false),
|
|
13
|
+
notifications: Joi.boolean().default(true)
|
|
14
|
+
}).optional().description('User preferences')
|
|
15
|
+
}).description('Example payload validation schema');
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const { FalconBaseWorker } = require('falconjs');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example worker - rename and customize as needed
|
|
5
|
+
*/
|
|
6
|
+
class ExampleWorker extends FalconBaseWorker {
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
super('example'); // Worker ID for MQTT topics
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Handle incoming MQTT messages
|
|
14
|
+
*/
|
|
15
|
+
async onMessage(topic, msg) {
|
|
16
|
+
try {
|
|
17
|
+
const message = JSON.parse(msg.toString());
|
|
18
|
+
|
|
19
|
+
switch (topic) {
|
|
20
|
+
case 'worker_example_job':
|
|
21
|
+
await this.processJob(message);
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
console.log(`Unhandled topic: ${topic}`);
|
|
25
|
+
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error processing message:', error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Process a job
|
|
33
|
+
*/
|
|
34
|
+
async processJob(job) {
|
|
35
|
+
const { jobId, data } = job;
|
|
36
|
+
|
|
37
|
+
console.log(`Processing job ${jobId}:`, data);
|
|
38
|
+
|
|
39
|
+
// Simulate work
|
|
40
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
41
|
+
|
|
42
|
+
// Update job status in database if available
|
|
43
|
+
if (this.models.job) {
|
|
44
|
+
await this.models.job.findByIdAndUpdate(jobId, {
|
|
45
|
+
status: 'completed',
|
|
46
|
+
completedAt: new Date(),
|
|
47
|
+
result: { processed: true }
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Send completion notification
|
|
52
|
+
await this.publish('worker_example_complete', {
|
|
53
|
+
jobId: jobId,
|
|
54
|
+
status: 'completed',
|
|
55
|
+
timestamp: new Date().toISOString()
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Worker main logic
|
|
61
|
+
*/
|
|
62
|
+
async run() {
|
|
63
|
+
const args = this.parseArgs();
|
|
64
|
+
console.log('Example worker started with args:', args);
|
|
65
|
+
|
|
66
|
+
// If specific job passed as argument, process it immediately
|
|
67
|
+
if (args.jobId) {
|
|
68
|
+
await this.processJob(args);
|
|
69
|
+
process.exit(0); // Exit after processing single job
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Otherwise, keep running and wait for MQTT jobs
|
|
73
|
+
console.log('Waiting for jobs...');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Start the worker if this file is run directly
|
|
78
|
+
if (require.main === module) {
|
|
79
|
+
const worker = new ExampleWorker();
|
|
80
|
+
worker.init().catch(console.error);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = ExampleWorker;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Falcon.js Application Entry Point
|
|
3
|
+
*/
|
|
4
|
+
require('dotenv').config();
|
|
5
|
+
const { FalconServer } = require("falconjs");
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
try {
|
|
9
|
+
/**
|
|
10
|
+
* Create the server instance with path to settings
|
|
11
|
+
*/
|
|
12
|
+
const server = new FalconServer(__dirname);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Optional: Set custom authentication strategy
|
|
16
|
+
*/
|
|
17
|
+
// server.setAuthStrategy(async (context) => {
|
|
18
|
+
// // Custom auth setup here
|
|
19
|
+
// });
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize the server and related modules
|
|
23
|
+
*/
|
|
24
|
+
await server.init();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Start the HTTP server
|
|
28
|
+
*/
|
|
29
|
+
await server.runServer();
|
|
30
|
+
|
|
31
|
+
console.log(`🚀 Falcon server running on http://${server.SETTINGS.http.host}:${server.SETTINGS.http.port}`);
|
|
32
|
+
console.log(`📚 API Documentation: http://${server.SETTINGS.http.host}:${server.SETTINGS.http.port}/documentation`);
|
|
33
|
+
console.log(`❤️ Health Check: http://${server.SETTINGS.http.host}:${server.SETTINGS.http.port}/health`);
|
|
34
|
+
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('❌ Failed to start server:', error);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
main();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-initialization script
|
|
3
|
+
* This runs after all Falcon components are initialized
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports.run = async function(context) {
|
|
7
|
+
const { logger, mqtt_client, models, server } = context;
|
|
8
|
+
|
|
9
|
+
logger.info("Running post-initialization script...");
|
|
10
|
+
|
|
11
|
+
// Example: Set up MQTT subscriptions for the main server
|
|
12
|
+
if (mqtt_client) {
|
|
13
|
+
// Subscribe to service responses
|
|
14
|
+
mqtt_client.subscribe("service_*_response", (err) => {
|
|
15
|
+
if (err) {
|
|
16
|
+
logger.error("Failed to subscribe to service responses:", err);
|
|
17
|
+
} else {
|
|
18
|
+
logger.info("Subscribed to service response topics");
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Subscribe to worker completions
|
|
23
|
+
mqtt_client.subscribe("worker_*_complete", (err) => {
|
|
24
|
+
if (err) {
|
|
25
|
+
logger.error("Failed to subscribe to worker completions:", err);
|
|
26
|
+
} else {
|
|
27
|
+
logger.info("Subscribed to worker completion topics");
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Handle incoming messages
|
|
32
|
+
mqtt_client.on("message", async function (topic, msg) {
|
|
33
|
+
try {
|
|
34
|
+
const message = JSON.parse(msg.toString());
|
|
35
|
+
logger.info(`Received message on ${topic}:`, message);
|
|
36
|
+
|
|
37
|
+
// Handle different message types
|
|
38
|
+
if (topic.includes('_response')) {
|
|
39
|
+
// Handle service responses
|
|
40
|
+
logger.info("Service response received:", message);
|
|
41
|
+
} else if (topic.includes('_complete')) {
|
|
42
|
+
// Handle worker completions
|
|
43
|
+
logger.info("Worker job completed:", message);
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
logger.error("Error processing MQTT message:", error);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Example: Add custom routes dynamically
|
|
52
|
+
if (server) {
|
|
53
|
+
server.route({
|
|
54
|
+
method: 'GET',
|
|
55
|
+
path: '/api/status',
|
|
56
|
+
options: {
|
|
57
|
+
tags: ['system'],
|
|
58
|
+
description: 'System status endpoint'
|
|
59
|
+
},
|
|
60
|
+
handler: async (request, h) => {
|
|
61
|
+
return {
|
|
62
|
+
status: 'running',
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
uptime: process.uptime(),
|
|
65
|
+
memory: process.memoryUsage(),
|
|
66
|
+
version: require('../package.json').version
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Example: Initialize background tasks
|
|
73
|
+
setInterval(() => {
|
|
74
|
+
logger.debug("Heartbeat - System is running");
|
|
75
|
+
}, 60000); // Every minute
|
|
76
|
+
|
|
77
|
+
logger.info("Post-initialization completed successfully");
|
|
78
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
module.exports.settings = {
|
|
2
|
+
name: "falcon-app",
|
|
3
|
+
http: {
|
|
4
|
+
host: process.env.HTTP_HOST || "localhost",
|
|
5
|
+
port: process.env.HTTP_PORT || 3000
|
|
6
|
+
},
|
|
7
|
+
database: {
|
|
8
|
+
mongodb: {
|
|
9
|
+
database: process.env.MONGODB_URL || "mongodb://localhost:27017/falcon_db"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
mqtt: {
|
|
13
|
+
internal: true,
|
|
14
|
+
external: false
|
|
15
|
+
},
|
|
16
|
+
log: {
|
|
17
|
+
appenders: {
|
|
18
|
+
file: {
|
|
19
|
+
type: "file",
|
|
20
|
+
filename: process.env.LOG_FILE_NAME || "logs/app.log",
|
|
21
|
+
maxLogSize: process.env.LOG_MAX_SIZE || "10M",
|
|
22
|
+
backups: 3
|
|
23
|
+
},
|
|
24
|
+
console: {
|
|
25
|
+
type: "console"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
categories: {
|
|
29
|
+
default: {
|
|
30
|
+
appenders: ["file", "console"],
|
|
31
|
+
level: process.env.LOG_LEVEL || "info"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
swagger: {
|
|
36
|
+
enabled: true,
|
|
37
|
+
path: "/documentation"
|
|
38
|
+
},
|
|
39
|
+
auth: {
|
|
40
|
+
// Custom Authentication (API Keys, etc.)
|
|
41
|
+
custom: {
|
|
42
|
+
name: 'api-key',
|
|
43
|
+
validate: async (token, request, context) => {
|
|
44
|
+
// Custom validation logic with access to context (logger, redis, models)
|
|
45
|
+
context.logger.info('Validating API key');
|
|
46
|
+
|
|
47
|
+
// Check cache first
|
|
48
|
+
const cacheKey = `api-key:${token}`;
|
|
49
|
+
if (context.redis) {
|
|
50
|
+
const cached = await context.redis.get(cacheKey);
|
|
51
|
+
if (cached) return JSON.parse(cached);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Validate against database or external service
|
|
55
|
+
if (context.models.apiKey) {
|
|
56
|
+
const apiKey = await context.models.apiKey.findOne({ key: token, active: true });
|
|
57
|
+
if (apiKey) {
|
|
58
|
+
const user = { id: apiKey.userId, roles: apiKey.roles };
|
|
59
|
+
// Cache for 5 minutes
|
|
60
|
+
if (context.redis) {
|
|
61
|
+
await context.redis.setEx(cacheKey, 300, JSON.stringify(user));
|
|
62
|
+
}
|
|
63
|
+
return user;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
},
|
|
69
|
+
extractToken: (request) => {
|
|
70
|
+
// Custom token extraction
|
|
71
|
+
return request.headers['x-api-key'] || request.query.apiKey;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// JWT Authentication
|
|
76
|
+
jwt: {
|
|
77
|
+
secret: process.env.JWT_SECRET || "your-secret-key-here",
|
|
78
|
+
algorithms: ['HS256'],
|
|
79
|
+
cache: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
ttl: 300 // 5 minutes
|
|
82
|
+
},
|
|
83
|
+
validate: async (decoded, request, context) => {
|
|
84
|
+
context.logger.info(`JWT validation for user: ${decoded.sub}`);
|
|
85
|
+
|
|
86
|
+
// Check if user exists and is active
|
|
87
|
+
if (context.models.user) {
|
|
88
|
+
const user = await context.models.user.findById(decoded.sub).select('-password');
|
|
89
|
+
if (user && user.active) {
|
|
90
|
+
return {
|
|
91
|
+
id: user._id,
|
|
92
|
+
email: user.email,
|
|
93
|
+
roles: user.roles || ['user']
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Fallback for development
|
|
99
|
+
return {
|
|
100
|
+
id: decoded.sub,
|
|
101
|
+
email: decoded.email,
|
|
102
|
+
roles: decoded.roles || ['user']
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// Cookie Authentication
|
|
108
|
+
cookie: {
|
|
109
|
+
password: process.env.COOKIE_SECRET || "your-32-char-cookie-password-here",
|
|
110
|
+
name: "falcon-session",
|
|
111
|
+
ttl: 24 * 60 * 60 * 1000, // 24 hours
|
|
112
|
+
isSecure: process.env.NODE_ENV === "production",
|
|
113
|
+
isHttpOnly: true,
|
|
114
|
+
validate: async (request, session, context) => {
|
|
115
|
+
context.logger.info(`Session validation for user: ${session.userId}`);
|
|
116
|
+
|
|
117
|
+
if (context.models.user) {
|
|
118
|
+
const user = await context.models.user.findById(session.userId).select('-password');
|
|
119
|
+
if (user && user.active) {
|
|
120
|
+
return {
|
|
121
|
+
id: user._id,
|
|
122
|
+
email: user.email,
|
|
123
|
+
roles: user.roles || ['user']
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return session.user;
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// JWKS Authentication (External Identity Providers)
|
|
133
|
+
jwks: {
|
|
134
|
+
jwksUri: process.env.JWKS_URI || "https://your-auth0-domain/.well-known/jwks.json",
|
|
135
|
+
issuer: process.env.JWT_ISSUER || "https://your-auth0-domain/",
|
|
136
|
+
audience: process.env.JWT_AUDIENCE || "your-api-identifier",
|
|
137
|
+
algorithms: ['RS256'],
|
|
138
|
+
cache: {
|
|
139
|
+
enabled: true,
|
|
140
|
+
ttl: 3600 // 1 hour
|
|
141
|
+
},
|
|
142
|
+
validate: async (decoded, request, context) => {
|
|
143
|
+
context.logger.info(`JWKS validation for user: ${decoded.sub}`);
|
|
144
|
+
|
|
145
|
+
// Map external identity to internal user
|
|
146
|
+
if (context.models.user) {
|
|
147
|
+
let user = await context.models.user.findOne({ externalId: decoded.sub });
|
|
148
|
+
|
|
149
|
+
if (!user && decoded.email) {
|
|
150
|
+
// Create user if doesn't exist
|
|
151
|
+
user = await context.models.user.create({
|
|
152
|
+
externalId: decoded.sub,
|
|
153
|
+
email: decoded.email,
|
|
154
|
+
name: decoded.name,
|
|
155
|
+
roles: ['user'],
|
|
156
|
+
active: true
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (user) {
|
|
161
|
+
return {
|
|
162
|
+
id: user._id,
|
|
163
|
+
email: user.email,
|
|
164
|
+
roles: user.roles || ['user']
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Fallback
|
|
170
|
+
return {
|
|
171
|
+
id: decoded.sub,
|
|
172
|
+
email: decoded.email,
|
|
173
|
+
roles: decoded['https://yourapp.com/roles'] || ['user']
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
// Socket.IO Configuration
|
|
179
|
+
socketio: {
|
|
180
|
+
enabled: true,
|
|
181
|
+
timeout: 5000
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
services: [],
|
|
185
|
+
workers: [],
|
|
186
|
+
models: [],
|
|
187
|
+
routes: [],
|
|
188
|
+
crud: {
|
|
189
|
+
exclude: []
|
|
190
|
+
},
|
|
191
|
+
postInit: "post"
|
|
192
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "falcon-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Falcon.js application",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node index.js",
|
|
8
|
+
"dev": "nodemon index.js",
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"falcon",
|
|
13
|
+
"nodejs",
|
|
14
|
+
"api"
|
|
15
|
+
],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"type": "commonjs",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"falconjs": "@triophore/falconjs",
|
|
21
|
+
"@hapi/boom": "^10.0.1",
|
|
22
|
+
"@hapi/inert": "^7.1.0",
|
|
23
|
+
"@hapi/vision": "^7.0.3",
|
|
24
|
+
"joi": "^17.13.3",
|
|
25
|
+
"jose": "^5.2.0",
|
|
26
|
+
"mongoose": "^8.3.1",
|
|
27
|
+
"redis": "^4.7.0",
|
|
28
|
+
"mqtt": "^5.13.3",
|
|
29
|
+
"log4js": "^6.9.1",
|
|
30
|
+
"rate-limiter-flexible": "^2.4.2",
|
|
31
|
+
"hapi-alive": "^2.0.4",
|
|
32
|
+
"hapi-swagger": "^17.2.1",
|
|
33
|
+
"dotenv": "^16.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"nodemon": "^3.1.10"
|
|
37
|
+
}
|
|
38
|
+
}
|