nebula-starter-kit 0.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 +107 -0
- package/dist/index.js +8 -0
- package/dist/run.js +112 -0
- package/dist/utils/addService.js +204 -0
- package/dist/utils/appName.js +75 -0
- package/dist/utils/deployNow.js +18 -0
- package/dist/utils/generateFiles.js +326 -0
- package/dist/utils/generateSchemas.js +58 -0
- package/dist/utils/listServices.js +24 -0
- package/dist/utils/replaceServiceNames.js +42 -0
- package/dist/utils/telemetryAddon.js +91 -0
- package/package.json +31 -0
- package/templates/core/audit/audit.controller.ts +125 -0
- package/templates/core/audit/audit.module.ts +10 -0
- package/templates/core/audit/audit.schema.ts +14 -0
- package/templates/core/audit/audit.service.ts +47 -0
- package/templates/core/auth/auth.controller.ts +207 -0
- package/templates/core/auth/auth.dto.ts +50 -0
- package/templates/core/auth/auth.module.ts +10 -0
- package/templates/core/auth/auth.service.ts +178 -0
- package/templates/core/auth/utils.ts +160 -0
- package/templates/core/constants/environment.db.ts +27 -0
- package/templates/core/constants/environment.module.ts +9 -0
- package/templates/core/constants/environment.service.ts +69 -0
- package/templates/core/core.controller.ts +35 -0
- package/templates/core/core.module.ts +22 -0
- package/templates/core/database/database.module.ts +20 -0
- package/templates/core/database/database.provider.ts +32 -0
- package/templates/core/database/database.service.ts +168 -0
- package/templates/core/database/database.types.ts +13 -0
- package/templates/core/filters/audit.decorator.ts +5 -0
- package/templates/core/filters/audit.interceptor.ts +74 -0
- package/templates/core/filters/http-exception.filter.ts +43 -0
- package/templates/core/filters/success-message.decorator.ts +5 -0
- package/templates/core/filters/success-response.interceptor.ts +35 -0
- package/templates/core/summarize/summarize.controller.ts +74 -0
- package/templates/core/summarize/summarize.dto.ts +13 -0
- package/templates/core/summarize/summarize.module.ts +9 -0
- package/templates/core/summarize/summarize.service.ts +54 -0
- package/templates/nest-cli.json +8 -0
- package/templates/package.json +52 -0
- package/templates/service/src/__name__.controller.ts +15 -0
- package/templates/service/src/__name__.module.ts +11 -0
- package/templates/service/src/__name__.schema.ts +15 -0
- package/templates/service/src/__name__.service.ts +12 -0
- package/templates/service/src/lambda.ts +60 -0
- package/templates/tsconfig.json +28 -0
- package/templates/ui/README.md +36 -0
- package/templates/ui/eslint.config.mjs +18 -0
- package/templates/ui/next.config.ts +8 -0
- package/templates/ui/package.json +33 -0
- package/templates/ui/postcss.config.mjs +7 -0
- package/templates/ui/public/file.svg +1 -0
- package/templates/ui/public/globe.svg +1 -0
- package/templates/ui/public/next.svg +1 -0
- package/templates/ui/public/vercel.svg +1 -0
- package/templates/ui/public/window.svg +1 -0
- package/templates/ui/src/app/LandingPage.tsx +98 -0
- package/templates/ui/src/app/ai/summarize/page.tsx +115 -0
- package/templates/ui/src/app/context/AuthContext.tsx +48 -0
- package/templates/ui/src/app/favicon.ico +0 -0
- package/templates/ui/src/app/globals.css +26 -0
- package/templates/ui/src/app/layout.tsx +37 -0
- package/templates/ui/src/app/page.tsx +7 -0
- package/templates/ui/src/app/services/page.tsx +99 -0
- package/templates/ui/src/components/Auth.css +252 -0
- package/templates/ui/src/components/Auth.tsx +455 -0
- package/templates/ui/src/components/Error.tsx +32 -0
- package/templates/ui/src/components/FormInput.tsx +77 -0
- package/templates/ui/src/components/Loading.tsx +10 -0
- package/templates/ui/src/components/Login.tsx +171 -0
- package/templates/ui/src/components/Popup.css +90 -0
- package/templates/ui/src/components/Signup.tsx +155 -0
- package/templates/ui/src/utils/axiosInstance.ts +37 -0
- package/templates/ui/src/utils/axiosRawInstance.ts +33 -0
- package/templates/ui/src/utils/util.constant.ts +0 -0
- package/templates/ui/src/utils/util.function.ts +165 -0
- package/templates/ui/src/utils/util.type.ts +64 -0
- package/templates/ui/src/utils/variables.ts +6 -0
- package/templates/ui/tailwind.config.js +8 -0
- package/templates/ui/tsconfig.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Nebula Logix Starter Kit
|
|
2
|
+
|
|
3
|
+
This is a downloadable & installable AWS production-ready AI-enabled package that serves essentially as a Software As A Service (SaaS), that users/clients can setup quickly in their AWS environment.
|
|
4
|
+
|
|
5
|
+
## Components
|
|
6
|
+
- cli/ directory to generate a full-fledged deployable application with a variety of modules and functionalities to choose from.
|
|
7
|
+
- templates/ directory containing the base NestJS application template.
|
|
8
|
+
- modules/ directory for the modules options.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
- NestJS framework for building scalable and maintainable server-side applications.
|
|
12
|
+
- AWS Lambda integration for serverless deployment.
|
|
13
|
+
- Pre-configured TypeScript support.
|
|
14
|
+
- Sample API endpoint to demonstrate functionality.
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
- Node.js (version 14 or higher)
|
|
18
|
+
- npm (version 6 or higher)
|
|
19
|
+
- AWS CLI configured with your credentials
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
Navigate to the 'cli' directory
|
|
25
|
+
|
|
26
|
+
$ RUN cd cli
|
|
27
|
+
$ RUN npm install
|
|
28
|
+
$ RUN nebula create <app-name>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Deployment
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
Navigate to the app directory
|
|
35
|
+
|
|
36
|
+
$ RUN cd <app-name>
|
|
37
|
+
$ RUN serverless deploy --config infra/api/serverless.yml
|
|
38
|
+
$ COPY the "ServiceEndpoint" value from the terminal output and update the NEXT_PUBLIC_API_URL of the .env.local of the ui directory
|
|
39
|
+
$ RUN npm run deploy
|
|
40
|
+
|
|
41
|
+
$ Navigate to the "WebsiteURL" of the buckets on the terminal output
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Teardown
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
Navigate to the app directory
|
|
48
|
+
|
|
49
|
+
$ RUN cd <app-name>
|
|
50
|
+
$ RUN npm run teardown
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Common Errors
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
❌ Not a nebula project
|
|
57
|
+
Usage: Run nebula command only in specified locations
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
❌ Unknown command
|
|
62
|
+
Available commands
|
|
63
|
+
1. nebula create <app-name>
|
|
64
|
+
2. nebula add service <service-name>
|
|
65
|
+
3. nebula list services
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
❌ Services directory not found
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
❌ Modules directory not found
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Example of the folder structure of the starter kit and an nlx app generated.
|
|
78
|
+
The ui directory contains the NextJS React App
|
|
79
|
+
|
|
80
|
+
The infra contains the api gateway, s3 buckets and other associated infrastructure setup.
|
|
81
|
+
|
|
82
|
+
The services contains the individual service modules as determined by the user.
|
|
83
|
+
|
|
84
|
+
The core has the database, authentication and other related features.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
├── cli
|
|
89
|
+
│ ├── dist
|
|
90
|
+
│ ├── nebula.json
|
|
91
|
+
│ ├── package-lock.json
|
|
92
|
+
│ ├── package.json
|
|
93
|
+
│ ├── src
|
|
94
|
+
│ ├── templates
|
|
95
|
+
│ └── tsconfig.json
|
|
96
|
+
├── nlx
|
|
97
|
+
│ ├── core
|
|
98
|
+
│ ├── infra
|
|
99
|
+
│ ├── nebula.json
|
|
100
|
+
│ ├── package.json
|
|
101
|
+
│ ├── serverless-compose.yml
|
|
102
|
+
│ ├── services
|
|
103
|
+
│ ├── tsconfig.json
|
|
104
|
+
│ └── ui
|
|
105
|
+
└── README.md
|
|
106
|
+
|
|
107
|
+
```
|
package/dist/index.js
ADDED
package/dist/run.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.run = run;
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const appName_1 = require("./utils/appName");
|
|
13
|
+
const deployNow_1 = require("./utils/deployNow");
|
|
14
|
+
const telemetryAddon_1 = require("./utils/telemetryAddon");
|
|
15
|
+
const generateFiles_1 = require("./utils/generateFiles");
|
|
16
|
+
const generateSchemas_1 = require("./utils/generateSchemas");
|
|
17
|
+
const ROOT = process.cwd();
|
|
18
|
+
const region = (0, telemetryAddon_1.getAwsRegion)();
|
|
19
|
+
console.log('\nregion', region, '\n');
|
|
20
|
+
let appNamePlaceHolder;
|
|
21
|
+
async function run() {
|
|
22
|
+
const appName = await (0, appName_1.promptAppName)();
|
|
23
|
+
appNamePlaceHolder = appName;
|
|
24
|
+
console.log('appNamePlaceHolder', appNamePlaceHolder);
|
|
25
|
+
const OUTPUT = path_1.default.join(ROOT, `../${appName}`);
|
|
26
|
+
if (await fs_extra_1.default.pathExists(OUTPUT)) {
|
|
27
|
+
console.log(`🧹 Cleaning ${appName} folder...\n`);
|
|
28
|
+
await fs_extra_1.default.remove(OUTPUT);
|
|
29
|
+
}
|
|
30
|
+
await fs_extra_1.default.ensureDir(OUTPUT);
|
|
31
|
+
console.log(`📁 Fresh ${appName} created\n`);
|
|
32
|
+
const { selectionMode } = await inquirer_1.default.prompt([
|
|
33
|
+
{
|
|
34
|
+
type: 'list',
|
|
35
|
+
name: 'selectionMode',
|
|
36
|
+
message: 'How would you like to define services?',
|
|
37
|
+
choices: ['Select from available modules', 'Type service names manually'],
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
let selectedServices = [];
|
|
41
|
+
const modulesDir = path_1.default.join(ROOT, './templates/modules');
|
|
42
|
+
if (!fs_extra_1.default.existsSync(modulesDir)) {
|
|
43
|
+
console.log(`❌ Modules directory not found\n`);
|
|
44
|
+
throw new Error('Modules directory not found');
|
|
45
|
+
// process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (selectionMode === 'Select from available modules') {
|
|
48
|
+
const { services } = await inquirer_1.default.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: 'checkbox',
|
|
51
|
+
name: 'services',
|
|
52
|
+
message: 'Select services',
|
|
53
|
+
choices: fs_extra_1.default.readdirSync(path_1.default.join(ROOT, './templates/modules')),
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
selectedServices = services;
|
|
57
|
+
}
|
|
58
|
+
if (selectionMode === 'Type service names manually') {
|
|
59
|
+
// TODO Validate service names for casing and other formats
|
|
60
|
+
const { servicesInput } = await inquirer_1.default.prompt([
|
|
61
|
+
{
|
|
62
|
+
type: 'input',
|
|
63
|
+
name: 'servicesInput',
|
|
64
|
+
message: 'Enter service names (comma separated, e.g. products,orders,users):',
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
const services = servicesInput
|
|
68
|
+
.split(',')
|
|
69
|
+
.map((s) => s.trim())
|
|
70
|
+
.filter(Boolean);
|
|
71
|
+
selectedServices = services;
|
|
72
|
+
}
|
|
73
|
+
// Move core module into app
|
|
74
|
+
const coreDir = path_1.default.join(OUTPUT, 'core');
|
|
75
|
+
await fs_extra_1.default.ensureDir(coreDir);
|
|
76
|
+
fs_extra_1.default.copySync(path_1.default.join(ROOT, 'templates/core'), coreDir);
|
|
77
|
+
// Move UI module into app
|
|
78
|
+
const uiDir = path_1.default.join(OUTPUT, 'ui');
|
|
79
|
+
await fs_extra_1.default.ensureDir(uiDir);
|
|
80
|
+
fs_extra_1.default.copySync(path_1.default.join(ROOT, 'templates/ui'), uiDir);
|
|
81
|
+
// Generate nebula json
|
|
82
|
+
(0, generateFiles_1.generateNebulaJson)(appName, selectedServices, OUTPUT);
|
|
83
|
+
(0, generateSchemas_1.generateSchemas)(OUTPUT);
|
|
84
|
+
// Generate root lambda
|
|
85
|
+
(0, generateFiles_1.generateRootLambda)(appName, selectedServices, OUTPUT);
|
|
86
|
+
// Generate serverless.yml for infra/api gateway
|
|
87
|
+
await (0, generateFiles_1.generateInfraApiFile)(appName, OUTPUT);
|
|
88
|
+
// Generate serverless.yml for infra/buckets
|
|
89
|
+
await (0, generateFiles_1.generateInfraBucketFile)(appName, OUTPUT);
|
|
90
|
+
// Generate serverless.yml for infra/cognito
|
|
91
|
+
await (0, generateFiles_1.generateInfraCognitoFile)(appName, OUTPUT);
|
|
92
|
+
// Move package.json into app
|
|
93
|
+
await fs_extra_1.default.copy(path_1.default.join(ROOT, 'templates/package.json'), path_1.default.join(OUTPUT, 'package.json'));
|
|
94
|
+
// Move tsconfig into app
|
|
95
|
+
await fs_extra_1.default.copy(path_1.default.join(ROOT, 'templates/tsconfig.json'), path_1.default.join(OUTPUT, 'tsconfig.json'));
|
|
96
|
+
// Handle content for services
|
|
97
|
+
await (0, generateFiles_1.generateServerlessFiles)(selectedServices, ROOT, OUTPUT, appNamePlaceHolder);
|
|
98
|
+
// Create env file for Root environment variables
|
|
99
|
+
(0, generateFiles_1.generateRootEnv)(OUTPUT);
|
|
100
|
+
// Create env file for NextJS environment variables
|
|
101
|
+
(0, generateFiles_1.generateNextJSEnv)(appName, uiDir, selectedServices);
|
|
102
|
+
// Prompt telemetry
|
|
103
|
+
await (0, telemetryAddon_1.promptTelemetry)(appName, selectedServices);
|
|
104
|
+
// 7. Install deps & deploy
|
|
105
|
+
const deployNow = await (0, deployNow_1.promptDeployNow)();
|
|
106
|
+
if (deployNow) {
|
|
107
|
+
(0, child_process_1.execSync)('npm install && npm run deploy', {
|
|
108
|
+
cwd: OUTPUT,
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.serverlessTemplate = void 0;
|
|
7
|
+
exports.addService = addService;
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const replaceServiceNames_1 = require("./replaceServiceNames");
|
|
11
|
+
const telemetryAddon_1 = require("./telemetryAddon");
|
|
12
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
13
|
+
const generateFiles_1 = require("./generateFiles");
|
|
14
|
+
const generateSchemas_1 = require("./generateSchemas");
|
|
15
|
+
const region = (0, telemetryAddon_1.getAwsRegion)();
|
|
16
|
+
function getServicePorts(service, appName) {
|
|
17
|
+
const ROOT = process.cwd();
|
|
18
|
+
let config;
|
|
19
|
+
let nebulaPath;
|
|
20
|
+
let httpPort = 3001;
|
|
21
|
+
let lambdaPort = 3001;
|
|
22
|
+
if (!appName) {
|
|
23
|
+
nebulaPath = path_1.default.join(ROOT, 'nebula.json');
|
|
24
|
+
if (!fs_extra_1.default.existsSync(nebulaPath)) {
|
|
25
|
+
throw new Error('Not a Nebula project');
|
|
26
|
+
}
|
|
27
|
+
config = fs_extra_1.default.readJsonSync(nebulaPath);
|
|
28
|
+
appName = config.name;
|
|
29
|
+
const servicesCount = config.services.length;
|
|
30
|
+
httpPort = 3001 + servicesCount * 2;
|
|
31
|
+
lambdaPort = httpPort + 1;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
nebulaPath = path_1.default.join(ROOT, `../${appName}`, 'nebula.json');
|
|
35
|
+
if (!fs_extra_1.default.existsSync(nebulaPath)) {
|
|
36
|
+
throw new Error('Not a Nebula project');
|
|
37
|
+
}
|
|
38
|
+
config = fs_extra_1.default.readJsonSync(nebulaPath);
|
|
39
|
+
const index = config.services.indexOf(service);
|
|
40
|
+
httpPort = 3001 + index * 2;
|
|
41
|
+
lambdaPort = httpPort + 1;
|
|
42
|
+
}
|
|
43
|
+
return { httpPort, lambdaPort, appName };
|
|
44
|
+
}
|
|
45
|
+
const serverlessTemplate = (service, appName) => {
|
|
46
|
+
const { httpPort, lambdaPort, appName: nameTag, } = getServicePorts(service, appName);
|
|
47
|
+
console.log('offline port values for service:', service, httpPort, lambdaPort, nameTag);
|
|
48
|
+
return `service: ${nameTag}-${service}
|
|
49
|
+
|
|
50
|
+
provider:
|
|
51
|
+
name: aws
|
|
52
|
+
runtime: nodejs20.x
|
|
53
|
+
region: ${region}
|
|
54
|
+
stage: \${opt:stage, 'dev'}
|
|
55
|
+
memorySize: 256
|
|
56
|
+
timeout: 30
|
|
57
|
+
|
|
58
|
+
environment:
|
|
59
|
+
REGION: us-east-1
|
|
60
|
+
COGNITO_CLIENT_ID: \${param:CognitoClientId, env:COGNITO_CLIENT_ID}
|
|
61
|
+
COGNITO_USER_POOL_ID: \${param:CognitoUserPoolId, env:COGNITO_USER_POOL_ID}
|
|
62
|
+
|
|
63
|
+
apiGateway:
|
|
64
|
+
restApiId: \${param:ApiGatewayRestApiId, null}
|
|
65
|
+
restApiRootResourceId: \${param:ApiGatewayRootResourceId, null}
|
|
66
|
+
|
|
67
|
+
stackTags:
|
|
68
|
+
CostCenter: \${self:provider.stage}-\${aws:accountId}
|
|
69
|
+
|
|
70
|
+
iam:
|
|
71
|
+
role:
|
|
72
|
+
statements:
|
|
73
|
+
# ✅ Bedrock permission
|
|
74
|
+
- Effect: Allow
|
|
75
|
+
Action:
|
|
76
|
+
- bedrock:InvokeModel
|
|
77
|
+
Resource:
|
|
78
|
+
- arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-haiku-20240307-v1:0
|
|
79
|
+
|
|
80
|
+
# ✅ Cognito permission
|
|
81
|
+
- Effect: Allow
|
|
82
|
+
Action:
|
|
83
|
+
- cognito-idp:ListUsers
|
|
84
|
+
Resource:
|
|
85
|
+
- arn:aws:cognito-idp:\${self:provider.region}:\${aws:accountId}:userpool/\${param:CognitoUserPoolId, env:COGNITO_USER_POOL_ID}
|
|
86
|
+
|
|
87
|
+
# ✅ DynamoDB permission
|
|
88
|
+
- Effect: Allow
|
|
89
|
+
Action:
|
|
90
|
+
- dynamodb:DescribeTable
|
|
91
|
+
- dynamodb:CreateTable
|
|
92
|
+
- dynamodb:GetItem
|
|
93
|
+
- dynamodb:PutItem
|
|
94
|
+
- dynamodb:UpdateItem
|
|
95
|
+
- dynamodb:Query
|
|
96
|
+
- dynamodb:Scan
|
|
97
|
+
Resource:
|
|
98
|
+
- arn:aws:dynamodb:\${self:provider.region}:*:table/*
|
|
99
|
+
|
|
100
|
+
# ✅ Cloudformation permission
|
|
101
|
+
- Effect: Allow
|
|
102
|
+
Action:
|
|
103
|
+
- cloudformation:ListStacks
|
|
104
|
+
- cloudformation:DescribeStacks
|
|
105
|
+
Resource: "*"
|
|
106
|
+
|
|
107
|
+
custom:
|
|
108
|
+
serverless-offline:
|
|
109
|
+
noPrependStageInUrl: true
|
|
110
|
+
httpPort: ${httpPort}
|
|
111
|
+
lambdaPort: ${lambdaPort}
|
|
112
|
+
|
|
113
|
+
build:
|
|
114
|
+
esbuild:
|
|
115
|
+
bundle: true
|
|
116
|
+
minify: false
|
|
117
|
+
sourcemap: true
|
|
118
|
+
keepNames: true
|
|
119
|
+
target: node20
|
|
120
|
+
format: cjs
|
|
121
|
+
|
|
122
|
+
external:
|
|
123
|
+
- aws-sdk
|
|
124
|
+
- "@nestjs/microservices"
|
|
125
|
+
- "@nestjs/microservices/microservices-module"
|
|
126
|
+
- "@nestjs/websockets"
|
|
127
|
+
- "@nestjs/websockets/socket-module"
|
|
128
|
+
- "class-transformer/storage"
|
|
129
|
+
|
|
130
|
+
functions:
|
|
131
|
+
app:
|
|
132
|
+
handler: src/lambda.handler
|
|
133
|
+
events:
|
|
134
|
+
- http:
|
|
135
|
+
path: /${service}/{proxy+}
|
|
136
|
+
method: any
|
|
137
|
+
cors: true
|
|
138
|
+
|
|
139
|
+
package:
|
|
140
|
+
individually: true
|
|
141
|
+
|
|
142
|
+
plugins:
|
|
143
|
+
- serverless-offline
|
|
144
|
+
`;
|
|
145
|
+
};
|
|
146
|
+
exports.serverlessTemplate = serverlessTemplate;
|
|
147
|
+
async function addService(serviceName, selectedServices, appName) {
|
|
148
|
+
const ROOT = process.cwd();
|
|
149
|
+
const nebulaPath = path_1.default.join(ROOT, 'nebula.json');
|
|
150
|
+
if (!fs_extra_1.default.existsSync(nebulaPath)) {
|
|
151
|
+
throw new Error('Not a Nebula project');
|
|
152
|
+
}
|
|
153
|
+
const config = fs_extra_1.default.readJsonSync(nebulaPath);
|
|
154
|
+
if (config.services.includes(serviceName)) {
|
|
155
|
+
console.log('Service already exists');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// create folder
|
|
159
|
+
const serviceDir = path_1.default.join(ROOT, 'services', serviceName);
|
|
160
|
+
await fs_extra_1.default.ensureDir(serviceDir);
|
|
161
|
+
// copy base nest template
|
|
162
|
+
fs_extra_1.default.copySync(path_1.default.join(ROOT, '../cli/templates/service'), serviceDir);
|
|
163
|
+
// update service names
|
|
164
|
+
await (0, replaceServiceNames_1.replaceServiceNames)(serviceDir, serviceName, appName);
|
|
165
|
+
// Generate serverless.yml per service
|
|
166
|
+
await fs_extra_1.default.writeFile(path_1.default.join(serviceDir, 'serverless.yml'), (0, exports.serverlessTemplate)(serviceName));
|
|
167
|
+
// Regenerate root lambda
|
|
168
|
+
(0, generateFiles_1.generateRootLambda)(config.name, [...selectedServices, serviceName], ROOT);
|
|
169
|
+
// Generate root serverless-compose.yml
|
|
170
|
+
const composePath = path_1.default.join(ROOT, 'serverless-compose.yml');
|
|
171
|
+
let parsed;
|
|
172
|
+
if (fs_extra_1.default.existsSync(composePath)) {
|
|
173
|
+
parsed = yaml_1.default.parse(await fs_extra_1.default.readFile(composePath, 'utf8'));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
parsed = {
|
|
177
|
+
services: {
|
|
178
|
+
api: { path: 'infra/api' },
|
|
179
|
+
cognito: { path: 'infra/cognito' },
|
|
180
|
+
buckets: { path: 'infra/buckets' },
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
selectedServices.push(serviceName);
|
|
185
|
+
for (const service of selectedServices) {
|
|
186
|
+
if (!parsed.services[service]) {
|
|
187
|
+
parsed.services[service] = {
|
|
188
|
+
path: `services/${service}`,
|
|
189
|
+
params: {
|
|
190
|
+
ApiGatewayRestApiId: '${api.ApiGatewayRestApiId}',
|
|
191
|
+
ApiGatewayRootResourceId: '${api.ApiGatewayRootResourceId}',
|
|
192
|
+
CognitoClientId: '${cognito.CognitoClientId}',
|
|
193
|
+
CognitoUserPoolId: '${cognito.CognitoUserPoolId}',
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
await fs_extra_1.default.writeFile(composePath, yaml_1.default.stringify(parsed));
|
|
199
|
+
// update config
|
|
200
|
+
config.services.push(serviceName);
|
|
201
|
+
fs_extra_1.default.writeJsonSync(nebulaPath, config, { spaces: 2 });
|
|
202
|
+
(0, generateSchemas_1.generateSchemas)(ROOT);
|
|
203
|
+
console.log(`✅ Service ${serviceName} created & added`);
|
|
204
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.promptAppName = promptAppName;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
const listServices_1 = require("./listServices");
|
|
11
|
+
const addService_1 = require("./addService");
|
|
12
|
+
async function promptAppName() {
|
|
13
|
+
const [, , command, argAppName] = process.argv;
|
|
14
|
+
const commands = ['create', 'add', 'list'];
|
|
15
|
+
const addListCommands = ['add', 'list'];
|
|
16
|
+
const nebulaConfig = path_1.default.join(process.cwd(), 'nebula.json');
|
|
17
|
+
if (!fs_extra_1.default.existsSync(nebulaConfig)) {
|
|
18
|
+
console.log(`❌ Not a nebula project\nUsage: Run nebula command only in specified locations\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const config = fs_extra_1.default.readJsonSync(nebulaConfig);
|
|
22
|
+
if (!commands.includes(command)) {
|
|
23
|
+
console.log(`❌ Unknown command\nAvailable commands\n1. nebula create <app-name>\n2. nebula add service <service-name>\n3. nebula list services\n`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
if (addListCommands.includes(command)) {
|
|
27
|
+
if (!config.services) {
|
|
28
|
+
console.log('❌ Services directory not found\n');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
switch (command) {
|
|
33
|
+
case 'add':
|
|
34
|
+
if (!process.argv[4]) {
|
|
35
|
+
console.log(`❌ Missing service name\nUsage: nebula add service <service-name>\n`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
if (argAppName === 'service')
|
|
39
|
+
await (0, addService_1.addService)(process.argv[4], config.services, `${config.name}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
case 'list':
|
|
42
|
+
if (argAppName !== 'services') {
|
|
43
|
+
console.log(`❌ Incorrect CLI command\nUsage: nebula list services\n`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
if (argAppName === 'services')
|
|
47
|
+
(0, listServices_1.listServices)();
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!config.services) {
|
|
53
|
+
if (config.name !== 'cli') {
|
|
54
|
+
console.log(`❌ Incorrect CLI directory\n`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
let appName = argAppName;
|
|
59
|
+
// If app name not provided → prompt
|
|
60
|
+
if (!appName) {
|
|
61
|
+
const { appName: promptedName } = await inquirer_1.default.prompt([
|
|
62
|
+
{
|
|
63
|
+
type: 'input',
|
|
64
|
+
name: 'appName',
|
|
65
|
+
message: 'Enter application name:',
|
|
66
|
+
default: 'my-starterkit-app',
|
|
67
|
+
validate: (input) => /^[a-z0-9-]+$/.test(input)
|
|
68
|
+
? true
|
|
69
|
+
: 'Use lowercase letters, numbers, and hyphens only',
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
return promptedName;
|
|
73
|
+
}
|
|
74
|
+
return appName;
|
|
75
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.promptDeployNow = promptDeployNow;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
async function promptDeployNow() {
|
|
9
|
+
const { deployNow } = await inquirer_1.default.prompt([
|
|
10
|
+
{
|
|
11
|
+
type: 'confirm',
|
|
12
|
+
name: 'deployNow',
|
|
13
|
+
message: 'Deploy to AWS using SAM now?',
|
|
14
|
+
default: false,
|
|
15
|
+
},
|
|
16
|
+
]);
|
|
17
|
+
return deployNow;
|
|
18
|
+
}
|