indraq_cli 1.5.8 → 1.9.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/LICENSE +21 -21
- package/README.md +1517 -1686
- package/cloud-api/.dockerignore +18 -0
- package/cloud-api/.env.example +14 -0
- package/cloud-api/Dockerfile +19 -0
- package/cloud-api/README.md +141 -0
- package/cloud-api/package-lock.json +2250 -0
- package/cloud-api/package.json +29 -0
- package/cloud-api/sql/001_init.sql +8 -0
- package/cloud-api/sql/002_user_providers_and_project_resources.sql +40 -0
- package/cloud-api/sql/003_environment_infrastructure.sql +19 -0
- package/cloud-api/sql/004_managed_aws_credentials.sql +20 -0
- package/cloud-api/sql/005_project_members_and_resource_grants.sql +22 -0
- package/cloud-api/sql/006_catalog_identity_password_security.sql +96 -0
- package/cloud-api/sql/007_aws_access_key_rotation.sql +16 -0
- package/cloud-api/src/index.ts +1254 -0
- package/cloud-api/tsconfig.json +18 -0
- package/dist/cli/create-program.d.ts.map +1 -1
- package/dist/cli/create-program.js +21 -3
- package/dist/cli/create-program.js.map +1 -1
- package/dist/cli/doctor.command.d.ts +1 -1
- package/dist/cli/doctor.command.d.ts.map +1 -1
- package/dist/cli/doctor.command.js +70 -12
- package/dist/cli/doctor.command.js.map +1 -1
- package/dist/cli/help-content.d.ts +1 -1
- package/dist/cli/help-content.d.ts.map +1 -1
- package/dist/cli/help-content.js +256 -177
- package/dist/cli/help-content.js.map +1 -1
- package/dist/modules/access/commands/access.command.d.ts +3 -0
- package/dist/modules/access/commands/access.command.d.ts.map +1 -0
- package/dist/modules/access/commands/access.command.js +226 -0
- package/dist/modules/access/commands/access.command.js.map +1 -0
- package/dist/modules/access/index.d.ts +3 -0
- package/dist/modules/access/index.d.ts.map +1 -0
- package/dist/modules/access/index.js +11 -0
- package/dist/modules/access/index.js.map +1 -0
- package/dist/modules/aws/commands/configure-aws.command.d.ts +2 -0
- package/dist/modules/aws/commands/configure-aws.command.d.ts.map +1 -0
- package/dist/modules/aws/commands/configure-aws.command.js +7 -0
- package/dist/modules/aws/commands/configure-aws.command.js.map +1 -0
- package/dist/modules/aws/commands/dns.command.d.ts +22 -0
- package/dist/modules/aws/commands/dns.command.d.ts.map +1 -0
- package/dist/modules/aws/commands/dns.command.js +229 -0
- package/dist/modules/aws/commands/dns.command.js.map +1 -0
- package/dist/modules/aws/commands/healthcheck.command.d.ts +22 -0
- package/dist/modules/aws/commands/healthcheck.command.d.ts.map +1 -0
- package/dist/modules/aws/commands/healthcheck.command.js +125 -0
- package/dist/modules/aws/commands/healthcheck.command.js.map +1 -0
- package/dist/modules/aws/commands/iam.command.d.ts +24 -0
- package/dist/modules/aws/commands/iam.command.d.ts.map +1 -0
- package/dist/modules/aws/commands/iam.command.js +219 -0
- package/dist/modules/aws/commands/iam.command.js.map +1 -0
- package/dist/modules/aws/config/aws-config.d.ts +23 -0
- package/dist/modules/aws/config/aws-config.d.ts.map +1 -0
- package/dist/modules/aws/config/aws-config.js +72 -0
- package/dist/modules/aws/config/aws-config.js.map +1 -0
- package/dist/modules/aws/index.d.ts +3 -0
- package/dist/modules/aws/index.d.ts.map +1 -0
- package/dist/modules/aws/index.js +54 -0
- package/dist/modules/aws/index.js.map +1 -0
- package/dist/modules/aws/services/aws-clients.d.ts +9 -0
- package/dist/modules/aws/services/aws-clients.d.ts.map +1 -0
- package/dist/modules/aws/services/aws-clients.js +43 -0
- package/dist/modules/aws/services/aws-clients.js.map +1 -0
- package/dist/modules/aws/services/route53-lookup.service.d.ts +9 -0
- package/dist/modules/aws/services/route53-lookup.service.d.ts.map +1 -0
- package/dist/modules/aws/services/route53-lookup.service.js +57 -0
- package/dist/modules/aws/services/route53-lookup.service.js.map +1 -0
- package/dist/modules/cloud/commands/cloud.command.d.ts +38 -0
- package/dist/modules/cloud/commands/cloud.command.d.ts.map +1 -0
- package/dist/modules/cloud/commands/cloud.command.js +443 -0
- package/dist/modules/cloud/commands/cloud.command.js.map +1 -0
- package/dist/modules/cloud/commands/credentials.command.d.ts +5 -0
- package/dist/modules/cloud/commands/credentials.command.d.ts.map +1 -0
- package/dist/modules/cloud/commands/credentials.command.js +25 -0
- package/dist/modules/cloud/commands/credentials.command.js.map +1 -0
- package/dist/modules/cloud/commands/environment.command.d.ts +6 -0
- package/dist/modules/cloud/commands/environment.command.d.ts.map +1 -0
- package/dist/modules/cloud/commands/environment.command.js +75 -0
- package/dist/modules/cloud/commands/environment.command.js.map +1 -0
- package/dist/modules/cloud/commands/provider.command.d.ts +3 -0
- package/dist/modules/cloud/commands/provider.command.d.ts.map +1 -0
- package/dist/modules/cloud/commands/provider.command.js +193 -0
- package/dist/modules/cloud/commands/provider.command.js.map +1 -0
- package/dist/modules/cloud/config/cloud-config.d.ts +19 -0
- package/dist/modules/cloud/config/cloud-config.d.ts.map +1 -0
- package/dist/modules/cloud/config/cloud-config.js +53 -0
- package/dist/modules/cloud/config/cloud-config.js.map +1 -0
- package/dist/modules/cloud/index.d.ts +3 -0
- package/dist/modules/cloud/index.d.ts.map +1 -0
- package/dist/modules/cloud/index.js +62 -0
- package/dist/modules/cloud/index.js.map +1 -0
- package/dist/modules/cloud/services/cloud-api.service.d.ts +17 -0
- package/dist/modules/cloud/services/cloud-api.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/cloud-api.service.js +61 -0
- package/dist/modules/cloud/services/cloud-api.service.js.map +1 -0
- package/dist/modules/cloud/services/cloud-aws.service.d.ts +23 -0
- package/dist/modules/cloud/services/cloud-aws.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/cloud-aws.service.js +26 -0
- package/dist/modules/cloud/services/cloud-aws.service.js.map +1 -0
- package/dist/modules/cloud/services/cloud-ghcr.service.d.ts +12 -0
- package/dist/modules/cloud/services/cloud-ghcr.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/cloud-ghcr.service.js +31 -0
- package/dist/modules/cloud/services/cloud-ghcr.service.js.map +1 -0
- package/dist/modules/cloud/services/cloud-jenkins.service.d.ts +29 -0
- package/dist/modules/cloud/services/cloud-jenkins.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/cloud-jenkins.service.js +236 -0
- package/dist/modules/cloud/services/cloud-jenkins.service.js.map +1 -0
- package/dist/modules/cloud/services/cloud-npm.service.d.ts +3 -0
- package/dist/modules/cloud/services/cloud-npm.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/cloud-npm.service.js +13 -0
- package/dist/modules/cloud/services/cloud-npm.service.js.map +1 -0
- package/dist/modules/cloud/services/environment.service.d.ts +25 -0
- package/dist/modules/cloud/services/environment.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/environment.service.js +21 -0
- package/dist/modules/cloud/services/environment.service.js.map +1 -0
- package/dist/modules/cloud/services/managed-aws-credential.service.d.ts +36 -0
- package/dist/modules/cloud/services/managed-aws-credential.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/managed-aws-credential.service.js +35 -0
- package/dist/modules/cloud/services/managed-aws-credential.service.js.map +1 -0
- package/dist/modules/cloud/services/project-cloud.service.d.ts +28 -0
- package/dist/modules/cloud/services/project-cloud.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/project-cloud.service.js +50 -0
- package/dist/modules/cloud/services/project-cloud.service.js.map +1 -0
- package/dist/modules/cloud/services/user-provider.service.d.ts +17 -0
- package/dist/modules/cloud/services/user-provider.service.d.ts.map +1 -0
- package/dist/modules/cloud/services/user-provider.service.js +25 -0
- package/dist/modules/cloud/services/user-provider.service.js.map +1 -0
- package/dist/modules/configure/configure.command.d.ts.map +1 -1
- package/dist/modules/configure/configure.command.js +17 -94
- package/dist/modules/configure/configure.command.js.map +1 -1
- package/dist/modules/deploy/commands/build.command.d.ts +1 -2
- package/dist/modules/deploy/commands/build.command.d.ts.map +1 -1
- package/dist/modules/deploy/commands/build.command.js +82 -124
- package/dist/modules/deploy/commands/build.command.js.map +1 -1
- package/dist/modules/deploy/index.d.ts.map +1 -1
- package/dist/modules/deploy/index.js +10 -30
- package/dist/modules/deploy/index.js.map +1 -1
- package/dist/modules/deploy/services/docker.service.d.ts +6 -3
- package/dist/modules/deploy/services/docker.service.d.ts.map +1 -1
- package/dist/modules/deploy/services/docker.service.js +8 -11
- package/dist/modules/deploy/services/docker.service.js.map +1 -1
- package/dist/modules/dockerfile/commands/create-dockerfile.command.d.ts +13 -0
- package/dist/modules/dockerfile/commands/create-dockerfile.command.d.ts.map +1 -0
- package/dist/modules/dockerfile/commands/create-dockerfile.command.js +39 -0
- package/dist/modules/dockerfile/commands/create-dockerfile.command.js.map +1 -0
- package/dist/modules/dockerfile/index.d.ts +3 -0
- package/dist/modules/dockerfile/index.d.ts.map +1 -0
- package/dist/modules/dockerfile/index.js +7 -0
- package/dist/modules/dockerfile/index.js.map +1 -0
- package/dist/modules/jenkins/commands/admin.command.d.ts +22 -0
- package/dist/modules/jenkins/commands/admin.command.d.ts.map +1 -0
- package/dist/modules/jenkins/commands/admin.command.js +128 -0
- package/dist/modules/jenkins/commands/admin.command.js.map +1 -0
- package/dist/modules/jenkins/commands/create-deployment.command.d.ts +13 -0
- package/dist/modules/jenkins/commands/create-deployment.command.d.ts.map +1 -0
- package/dist/modules/jenkins/commands/create-deployment.command.js +115 -0
- package/dist/modules/jenkins/commands/create-deployment.command.js.map +1 -0
- package/dist/modules/jenkins/index.d.ts +3 -0
- package/dist/modules/jenkins/index.d.ts.map +1 -0
- package/dist/modules/jenkins/index.js +28 -0
- package/dist/modules/jenkins/index.js.map +1 -0
- package/dist/modules/mobile/commands/build-mobile.command.d.ts.map +1 -1
- package/dist/modules/mobile/commands/build-mobile.command.js +4 -22
- package/dist/modules/mobile/commands/build-mobile.command.js.map +1 -1
- package/dist/modules/mobile/commands/configure-mobile.command.d.ts +1 -1
- package/dist/modules/mobile/commands/configure-mobile.command.d.ts.map +1 -1
- package/dist/modules/mobile/commands/configure-mobile.command.js +11 -9
- package/dist/modules/mobile/commands/configure-mobile.command.js.map +1 -1
- package/dist/modules/mobile/services/jenkins-mobile.service.d.ts +2 -2
- package/dist/modules/mobile/services/jenkins-mobile.service.d.ts.map +1 -1
- package/dist/modules/mobile/services/jenkins-mobile.service.js +1 -2
- package/dist/modules/mobile/services/jenkins-mobile.service.js.map +1 -1
- package/dist/modules/mobile/services/project.service.d.ts +2 -2
- package/dist/modules/mobile/services/project.service.d.ts.map +1 -1
- package/dist/modules/prebuild/commands/prebuild.command.d.ts +13 -0
- package/dist/modules/prebuild/commands/prebuild.command.d.ts.map +1 -0
- package/dist/modules/prebuild/commands/prebuild.command.js +507 -0
- package/dist/modules/prebuild/commands/prebuild.command.js.map +1 -0
- package/dist/modules/prebuild/index.d.ts +3 -0
- package/dist/modules/prebuild/index.d.ts.map +1 -0
- package/dist/modules/prebuild/index.js +19 -0
- package/dist/modules/prebuild/index.js.map +1 -0
- package/dist/modules/proxy/commands/configure-proxy.command.d.ts +2 -0
- package/dist/modules/proxy/commands/configure-proxy.command.d.ts.map +1 -0
- package/dist/modules/proxy/commands/configure-proxy.command.js +7 -0
- package/dist/modules/proxy/commands/configure-proxy.command.js.map +1 -0
- package/dist/modules/proxy/commands/npm-user.command.d.ts +28 -0
- package/dist/modules/proxy/commands/npm-user.command.d.ts.map +1 -0
- package/dist/modules/proxy/commands/npm-user.command.js +77 -0
- package/dist/modules/proxy/commands/npm-user.command.js.map +1 -0
- package/dist/modules/proxy/commands/proxy.command.d.ts +22 -0
- package/dist/modules/proxy/commands/proxy.command.d.ts.map +1 -0
- package/dist/modules/proxy/commands/proxy.command.js +173 -0
- package/dist/modules/proxy/commands/proxy.command.js.map +1 -0
- package/dist/modules/proxy/config/npm-config.d.ts +17 -0
- package/dist/modules/proxy/config/npm-config.d.ts.map +1 -0
- package/dist/modules/proxy/config/npm-config.js +50 -0
- package/dist/modules/proxy/config/npm-config.js.map +1 -0
- package/dist/modules/proxy/index.d.ts +3 -0
- package/dist/modules/proxy/index.d.ts.map +1 -0
- package/dist/modules/proxy/index.js +21 -0
- package/dist/modules/proxy/index.js.map +1 -0
- package/dist/modules/proxy/services/npm-api.service.d.ts +76 -0
- package/dist/modules/proxy/services/npm-api.service.d.ts.map +1 -0
- package/dist/modules/proxy/services/npm-api.service.js +82 -0
- package/dist/modules/proxy/services/npm-api.service.js.map +1 -0
- package/dist/modules/scaffold/commands/init.command.d.ts +6 -0
- package/dist/modules/scaffold/commands/init.command.d.ts.map +1 -0
- package/dist/modules/scaffold/commands/init.command.js +193 -0
- package/dist/modules/scaffold/commands/init.command.js.map +1 -0
- package/dist/modules/scaffold/config/project-metadata.d.ts +9 -0
- package/dist/modules/scaffold/config/project-metadata.d.ts.map +1 -0
- package/dist/modules/scaffold/config/project-metadata.js +75 -0
- package/dist/modules/scaffold/config/project-metadata.js.map +1 -0
- package/dist/modules/scaffold/index.d.ts +3 -0
- package/dist/modules/scaffold/index.d.ts.map +1 -0
- package/dist/modules/scaffold/index.js +7 -0
- package/dist/modules/scaffold/index.js.map +1 -0
- package/dist/modules/scaffold/services/docker-template.service.d.ts +3 -0
- package/dist/modules/scaffold/services/docker-template.service.d.ts.map +1 -0
- package/dist/modules/scaffold/services/docker-template.service.js +131 -0
- package/dist/modules/scaffold/services/docker-template.service.js.map +1 -0
- package/dist/modules/scaffold/services/filesystem.service.d.ts +7 -0
- package/dist/modules/scaffold/services/filesystem.service.d.ts.map +1 -0
- package/dist/modules/scaffold/services/filesystem.service.js +97 -0
- package/dist/modules/scaffold/services/filesystem.service.js.map +1 -0
- package/dist/modules/scaffold/services/project-template.service.d.ts +4 -0
- package/dist/modules/scaffold/services/project-template.service.d.ts.map +1 -0
- package/dist/modules/scaffold/services/project-template.service.js +82 -0
- package/dist/modules/scaffold/services/project-template.service.js.map +1 -0
- package/dist/modules/scaffold/types.d.ts +65 -0
- package/dist/modules/scaffold/types.d.ts.map +1 -0
- package/dist/modules/scaffold/types.js +3 -0
- package/dist/modules/scaffold/types.js.map +1 -0
- package/dist/modules/users/commands/user.command.d.ts +22 -0
- package/dist/modules/users/commands/user.command.d.ts.map +1 -0
- package/dist/modules/users/commands/user.command.js +350 -0
- package/dist/modules/users/commands/user.command.js.map +1 -0
- package/dist/modules/users/index.d.ts +3 -0
- package/dist/modules/users/index.d.ts.map +1 -0
- package/dist/modules/users/index.js +34 -0
- package/dist/modules/users/index.js.map +1 -0
- package/dist/modules/users/providers/aws-user.provider.d.ts +11 -0
- package/dist/modules/users/providers/aws-user.provider.d.ts.map +1 -0
- package/dist/modules/users/providers/aws-user.provider.js +129 -0
- package/dist/modules/users/providers/aws-user.provider.js.map +1 -0
- package/dist/modules/users/providers/jenkins-user.provider.d.ts +6 -0
- package/dist/modules/users/providers/jenkins-user.provider.d.ts.map +1 -0
- package/dist/modules/users/providers/jenkins-user.provider.js +97 -0
- package/dist/modules/users/providers/jenkins-user.provider.js.map +1 -0
- package/dist/modules/users/providers/npm-user.provider.d.ts +4 -0
- package/dist/modules/users/providers/npm-user.provider.d.ts.map +1 -0
- package/dist/modules/users/providers/npm-user.provider.js +47 -0
- package/dist/modules/users/providers/npm-user.provider.js.map +1 -0
- package/dist/shared/config/config-files.d.ts.map +1 -1
- package/dist/shared/config/config-files.js +14 -10
- package/dist/shared/config/config-files.js.map +1 -1
- package/dist/shared/github/github-api.service.d.ts.map +1 -1
- package/dist/shared/github/github-api.service.js +25 -37
- package/dist/shared/github/github-api.service.js.map +1 -1
- package/dist/shared/jenkins/jenkins.service.d.ts +2 -0
- package/dist/shared/jenkins/jenkins.service.d.ts.map +1 -1
- package/dist/shared/jenkins/jenkins.service.js +30 -1
- package/dist/shared/jenkins/jenkins.service.js.map +1 -1
- package/dist/shared/listing.d.ts +9 -0
- package/dist/shared/listing.d.ts.map +1 -0
- package/dist/shared/listing.js +29 -0
- package/dist/shared/listing.js.map +1 -0
- package/docs/ARCHITECTURE.md +29 -0
- package/docs/CREATE-DEPLOYMENT-reference.groovy +1105 -0
- package/package.json +76 -59
- package/templates/jenkins/CREATE-DEPLOYMENT.groovy +1105 -0
- package/templates/jenkins/Jenkinsfile-Mobile-App +759 -759
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-clients.d.ts","sourceRoot":"","sources":["../../../../src/modules/aws/services/aws-clients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGzD,eAAO,MAAM,gBAAgB,QAAO;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAalG,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,MAejD,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describeAwsError = exports.createAwsClients = void 0;
|
|
4
|
+
const client_iam_1 = require("@aws-sdk/client-iam");
|
|
5
|
+
const client_route_53_1 = require("@aws-sdk/client-route-53");
|
|
6
|
+
const aws_config_1 = require("../config/aws-config");
|
|
7
|
+
const createAwsClients = () => {
|
|
8
|
+
const { project, local } = (0, aws_config_1.requireAwsConfig)();
|
|
9
|
+
const credentials = {
|
|
10
|
+
accessKeyId: local.accessKeyId,
|
|
11
|
+
secretAccessKey: local.secretAccessKey,
|
|
12
|
+
...(local.sessionToken ? { sessionToken: local.sessionToken } : {})
|
|
13
|
+
};
|
|
14
|
+
const result = {
|
|
15
|
+
iam: new client_iam_1.IAMClient({ region: project.region, credentials }),
|
|
16
|
+
route53: new client_route_53_1.Route53Client({ region: project.region, credentials })
|
|
17
|
+
};
|
|
18
|
+
if (project.hostedZoneId)
|
|
19
|
+
result.hostedZoneId = project.hostedZoneId;
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
exports.createAwsClients = createAwsClients;
|
|
23
|
+
const describeAwsError = (error) => {
|
|
24
|
+
if (!error || typeof error !== 'object')
|
|
25
|
+
return String(error);
|
|
26
|
+
const candidate = error;
|
|
27
|
+
const name = typeof candidate.name === 'string' ? candidate.name : 'AWS error';
|
|
28
|
+
const message = typeof candidate.message === 'string' ? candidate.message : 'Request failed';
|
|
29
|
+
const status = candidate.$metadata?.httpStatusCode;
|
|
30
|
+
if (name === 'AccessDenied' || name === 'AccessDeniedException' || status === 403) {
|
|
31
|
+
return `AWS permission denied: ${message}`;
|
|
32
|
+
}
|
|
33
|
+
if (name === 'InvalidClientTokenId' || name === 'SignatureDoesNotMatch' || name === 'UnrecognizedClientException') {
|
|
34
|
+
return `AWS authentication failed: ${message}`;
|
|
35
|
+
}
|
|
36
|
+
if (name === 'EntityAlreadyExists' || name === 'InvalidChangeBatch')
|
|
37
|
+
return `AWS rejected a duplicate or conflicting resource: ${message}`;
|
|
38
|
+
if (name === 'NoSuchEntity' || name === 'HostedZoneNotFound')
|
|
39
|
+
return `AWS resource not found: ${message}`;
|
|
40
|
+
return `${name}${status ? ` (HTTP ${status})` : ''}: ${message}`;
|
|
41
|
+
};
|
|
42
|
+
exports.describeAwsError = describeAwsError;
|
|
43
|
+
//# sourceMappingURL=aws-clients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-clients.js","sourceRoot":"","sources":["../../../../src/modules/aws/services/aws-clients.ts"],"names":[],"mappings":";;;AAAA,oDAAgD;AAChD,8DAAyD;AACzD,qDAAwD;AAEjD,MAAM,gBAAgB,GAAG,GAAsE,EAAE;IACtG,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAA,6BAAgB,GAAE,CAAC;IAC9C,MAAM,WAAW,GAAG;QAClB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC;IACF,MAAM,MAAM,GAAsE;QAChF,GAAG,EAAE,IAAI,sBAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;QAC3D,OAAO,EAAE,IAAI,+BAAa,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;KACpE,CAAC;IACF,IAAI,OAAO,CAAC,YAAY;QAAE,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IACrE,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAbW,QAAA,gBAAgB,oBAa3B;AAEK,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAU,EAAE;IACzD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,KAAuF,CAAC;IAC1G,MAAM,IAAI,GAAG,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IAC/E,MAAM,OAAO,GAAG,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAC7F,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,EAAE,cAAc,CAAC;IACnD,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,uBAAuB,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QAClF,OAAO,0BAA0B,OAAO,EAAE,CAAC;IAC7C,CAAC;IACD,IAAI,IAAI,KAAK,sBAAsB,IAAI,IAAI,KAAK,uBAAuB,IAAI,IAAI,KAAK,6BAA6B,EAAE,CAAC;QAClH,OAAO,8BAA8B,OAAO,EAAE,CAAC;IACjD,CAAC;IACD,IAAI,IAAI,KAAK,qBAAqB,IAAI,IAAI,KAAK,oBAAoB;QAAE,OAAO,qDAAqD,OAAO,EAAE,CAAC;IAC3I,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,oBAAoB;QAAE,OAAO,2BAA2B,OAAO,EAAE,CAAC;IAC1G,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,EAAE,CAAC;AACnE,CAAC,CAAC;AAfW,QAAA,gBAAgB,oBAe3B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ResourceRecordSet } from '@aws-sdk/client-route-53';
|
|
2
|
+
export interface Route53RecordMatch {
|
|
3
|
+
zoneId: string;
|
|
4
|
+
zoneName: string;
|
|
5
|
+
record: ResourceRecordSet;
|
|
6
|
+
}
|
|
7
|
+
export declare const findExactRoute53Record: (domain: string) => Promise<Route53RecordMatch | null>;
|
|
8
|
+
export declare const requireRoute53Record: (domain: string) => Promise<Route53RecordMatch>;
|
|
9
|
+
//# sourceMappingURL=route53-lookup.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route53-lookup.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/aws/services/route53-lookup.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyD,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAQzH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,eAAO,MAAM,sBAAsB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAqC9F,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,kBAAkB,CAIrF,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireRoute53Record = exports.findExactRoute53Record = void 0;
|
|
4
|
+
const client_route_53_1 = require("@aws-sdk/client-route-53");
|
|
5
|
+
const cloud_aws_service_1 = require("../../cloud/services/cloud-aws.service");
|
|
6
|
+
const project_metadata_1 = require("../../scaffold/config/project-metadata");
|
|
7
|
+
const project_cloud_service_1 = require("../../cloud/services/project-cloud.service");
|
|
8
|
+
const normalize = (value) => value.trim().toLowerCase().replace(/\.$/, '');
|
|
9
|
+
const zoneId = (value) => value.replace(/^\/hostedzone\//, '');
|
|
10
|
+
const findExactRoute53Record = async (domain) => {
|
|
11
|
+
const target = normalize(domain);
|
|
12
|
+
// Prefer the IndraQ project inventory. This lets normal users verify a DNS
|
|
13
|
+
// prerequisite without granting Route53 ListResourceRecordSets, which would
|
|
14
|
+
// expose all records in the hosted zone.
|
|
15
|
+
if ((0, project_metadata_1.getProjectReference)()?.projectId) {
|
|
16
|
+
try {
|
|
17
|
+
const resources = await (0, project_cloud_service_1.listProjectResources)();
|
|
18
|
+
const projectDns = resources.find((resource) => resource.provider === 'aws' && resource.resource_type.startsWith('route53-') && normalize(String(resource.config?.domain || '')) === target);
|
|
19
|
+
if (projectDns) {
|
|
20
|
+
const config = projectDns.config;
|
|
21
|
+
return {
|
|
22
|
+
zoneId: String(config.zoneId || ''),
|
|
23
|
+
zoneName: target.split('.').slice(1).join('.'),
|
|
24
|
+
record: { Name: domain.endsWith('.') ? domain : `${domain}.`, Type: (config.recordType || 'A') }
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch { /* Fall back to live AWS lookup for operators/unlinked folders. */ }
|
|
29
|
+
}
|
|
30
|
+
const { route53 } = await (0, cloud_aws_service_1.createCloudAwsClients)('Verify Route53 DNS record before NPM proxy creation');
|
|
31
|
+
const zonesOutput = await route53.send(new client_route_53_1.ListHostedZonesCommand({}));
|
|
32
|
+
const zones = (zonesOutput.HostedZones || [])
|
|
33
|
+
.filter((z) => z.Id && z.Name)
|
|
34
|
+
.map((z) => ({ id: zoneId(z.Id), name: normalize(z.Name) }))
|
|
35
|
+
.filter((z) => target === z.name || target.endsWith(`.${z.name}`))
|
|
36
|
+
.sort((a, b) => b.name.length - a.name.length);
|
|
37
|
+
for (const zone of zones) {
|
|
38
|
+
const output = await route53.send(new client_route_53_1.ListResourceRecordSetsCommand({
|
|
39
|
+
HostedZoneId: zone.id,
|
|
40
|
+
StartRecordName: domain.endsWith('.') ? domain : `${domain}.`,
|
|
41
|
+
MaxItems: 20
|
|
42
|
+
}));
|
|
43
|
+
const record = (output.ResourceRecordSets || []).find((r) => normalize(r.Name || '') === target);
|
|
44
|
+
if (record)
|
|
45
|
+
return { zoneId: zone.id, zoneName: zone.name, record };
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
49
|
+
exports.findExactRoute53Record = findExactRoute53Record;
|
|
50
|
+
const requireRoute53Record = async (domain) => {
|
|
51
|
+
const match = await (0, exports.findExactRoute53Record)(domain);
|
|
52
|
+
if (!match)
|
|
53
|
+
throw new Error(`No matching Route53 record exists for ${domain}. Create the AWS DNS record first with \`indraq dns create ${domain}\`; NPM proxy creation is blocked until DNS exists.`);
|
|
54
|
+
return match;
|
|
55
|
+
};
|
|
56
|
+
exports.requireRoute53Record = requireRoute53Record;
|
|
57
|
+
//# sourceMappingURL=route53-lookup.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route53-lookup.service.js","sourceRoot":"","sources":["../../../../src/modules/aws/services/route53-lookup.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type ListCliOptions } from '../../../shared/listing';
|
|
2
|
+
export declare const loginCloud: (options: {
|
|
3
|
+
email?: string;
|
|
4
|
+
password?: string;
|
|
5
|
+
}) => Promise<void>;
|
|
6
|
+
export interface PasswordResetOptions {
|
|
7
|
+
email?: string;
|
|
8
|
+
target?: string;
|
|
9
|
+
cloud?: boolean;
|
|
10
|
+
npm?: boolean;
|
|
11
|
+
jenkins?: boolean;
|
|
12
|
+
all?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare const resetPasswordCloud: (options: PasswordResetOptions) => Promise<void>;
|
|
15
|
+
export declare const logoutCloud: () => void;
|
|
16
|
+
export declare const whoamiCloud: () => Promise<void>;
|
|
17
|
+
export declare const listLogs: (options?: ListCliOptions) => Promise<void>;
|
|
18
|
+
export declare const createServer: (defaultEnvironment?: string) => Promise<void>;
|
|
19
|
+
export declare const listServers: () => Promise<void>;
|
|
20
|
+
export declare const syncProject: () => Promise<void>;
|
|
21
|
+
interface ProjectCreateOptions {
|
|
22
|
+
kind?: string;
|
|
23
|
+
framework?: string;
|
|
24
|
+
port?: string;
|
|
25
|
+
health?: string;
|
|
26
|
+
nodeVersion?: string;
|
|
27
|
+
image?: string;
|
|
28
|
+
reason?: string;
|
|
29
|
+
yes?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare const createProject: (requestedName: string | undefined, options: ProjectCreateOptions) => Promise<void>;
|
|
32
|
+
export declare const projectStatus: (environment?: string) => Promise<void>;
|
|
33
|
+
export declare const listProjects: (options: ListCliOptions) => Promise<void>;
|
|
34
|
+
export declare const linkProject: (options?: ListCliOptions) => Promise<void>;
|
|
35
|
+
export declare const addProjectUser: () => Promise<void>;
|
|
36
|
+
export declare const deleteProjectUser: () => Promise<void>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=cloud.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud.command.d.ts","sourceRoot":"","sources":["../../../../src/modules/cloud/commands/cloud.command.ts"],"names":[],"mappings":"AASA,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE3E,eAAO,MAAM,UAAU,GAAU,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,OAAO,CAAC,IAAI,CAoB7F,CAAC;AAEF,MAAM,WAAW,oBAAoB;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CAAE;AAE5I,eAAO,MAAM,kBAAkB,GAAU,SAAS,oBAAoB,KAAG,OAAO,CAAC,IAAI,CAuCpF,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,IAI9B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CAAiE,CAAC;AACnH,eAAO,MAAM,QAAQ,GAAU,UAAS,cAAmB,KAAG,OAAO,CAAC,IAAI,CAGzE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,2BAAiC,KAAG,OAAO,CAAC,IAAI,CAelF,CAAC;AACF,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CAA0E,CAAC;AAE5H,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CA2BhD,CAAC;AAIF,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAwKD,eAAO,MAAM,aAAa,GAAU,eAAe,MAAM,GAAG,SAAS,EAAE,SAAS,oBAAoB,KAAG,OAAO,CAAC,IAAI,CAoDlH,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,cAAc,MAAM,KAAG,OAAO,CAAC,IAAI,CAWtE,CAAC;AAaF,eAAO,MAAM,YAAY,GAAU,SAAS,cAAc,KAAG,OAAO,CAAC,IAAI,CAGxE,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,UAAS,cAAmB,KAAG,OAAO,CAAC,IAAI,CAmB5E,CAAC;AAEF,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,IAAI,CAcnD,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAUtD,CAAC"}
|
|
@@ -0,0 +1,443 @@
|
|
|
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.deleteProjectUser = exports.addProjectUser = exports.linkProject = exports.listProjects = exports.projectStatus = exports.createProject = exports.syncProject = exports.listServers = exports.createServer = exports.listLogs = exports.whoamiCloud = exports.logoutCloud = exports.resetPasswordCloud = exports.loginCloud = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
+
const cloud_config_1 = require("../config/cloud-config");
|
|
10
|
+
const cloud_api_service_1 = require("../services/cloud-api.service");
|
|
11
|
+
const project_metadata_1 = require("../../scaffold/config/project-metadata");
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const project_cloud_service_1 = require("../services/project-cloud.service");
|
|
15
|
+
const listing_1 = require("../../../shared/listing");
|
|
16
|
+
const loginCloud = async (options) => {
|
|
17
|
+
let email = options.email, password = options.password;
|
|
18
|
+
const answer = await inquirer_1.default.prompt([
|
|
19
|
+
{ type: 'input', name: 'email', message: 'Email:', when: !email, validate: (v) => /^\S+@\S+\.\S+$/.test(v) || 'Enter a valid email.' },
|
|
20
|
+
{ type: 'password', name: 'password', message: 'Password:', mask: '*', when: !password }
|
|
21
|
+
]);
|
|
22
|
+
email = email || answer.email;
|
|
23
|
+
password = password || answer.password;
|
|
24
|
+
const api = new cloud_api_service_1.CloudApiService();
|
|
25
|
+
const user = await api.login(email, password);
|
|
26
|
+
console.log(chalk_1.default.green('✓ Logged in to IndraQ Cloud.'));
|
|
27
|
+
if (user.mustChangePassword) {
|
|
28
|
+
console.log(chalk_1.default.yellow('Your current password is temporary and must be changed before you can use IndraQ.'));
|
|
29
|
+
const changed = await inquirer_1.default.prompt([
|
|
30
|
+
{ type: 'password', name: 'password', message: 'Create your new password:', mask: '*', validate: (v) => v.length >= 10 || 'Use at least 10 characters.' },
|
|
31
|
+
{ type: 'password', name: 'confirm', message: 'Confirm your new password:', mask: '*', validate: (v, answers) => v === answers.password || 'Passwords do not match.' }
|
|
32
|
+
]);
|
|
33
|
+
const result = await api.request('/auth/first-login-password', { method: 'POST', body: JSON.stringify({ password: changed.password }) });
|
|
34
|
+
console.log(chalk_1.default.green('✓ Password changed. Your temporary password can no longer be used.'));
|
|
35
|
+
if (result.results?.length > 1)
|
|
36
|
+
console.table(result.results);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
exports.loginCloud = loginCloud;
|
|
40
|
+
const resetPasswordCloud = async (options) => {
|
|
41
|
+
let email = options.email?.trim();
|
|
42
|
+
if (!email)
|
|
43
|
+
email = (await inquirer_1.default.prompt([{ type: 'input', name: 'email', message: 'Email:', validate: (v) => /^\S+@\S+\.\S+$/.test(v) || 'Enter a valid email.' }])).email.trim();
|
|
44
|
+
let targets = [];
|
|
45
|
+
if (options.all || options.target === 'all')
|
|
46
|
+
targets = ['cloud', 'npm', 'jenkins'];
|
|
47
|
+
else {
|
|
48
|
+
if (options.target)
|
|
49
|
+
targets.push(options.target.toLowerCase());
|
|
50
|
+
if (options.cloud)
|
|
51
|
+
targets.push('cloud');
|
|
52
|
+
if (options.npm)
|
|
53
|
+
targets.push('npm');
|
|
54
|
+
if (options.jenkins)
|
|
55
|
+
targets.push('jenkins');
|
|
56
|
+
targets = [...new Set(targets)];
|
|
57
|
+
}
|
|
58
|
+
const invalid = targets.filter((v) => !['cloud', 'npm', 'jenkins'].includes(v));
|
|
59
|
+
if (invalid.length)
|
|
60
|
+
throw new Error(`Unsupported password target: ${invalid.join(', ')}. AWS uses access keys, not passwords.`);
|
|
61
|
+
if (!targets.length) {
|
|
62
|
+
const chosen = await inquirer_1.default.prompt([{ type: 'checkbox', name: 'targets', message: 'Reset password for:', choices: [
|
|
63
|
+
{ name: 'INDRAQ CLOUD', value: 'cloud', checked: true },
|
|
64
|
+
{ name: 'Nginx Proxy Manager', value: 'npm' },
|
|
65
|
+
{ name: 'Jenkins (managed accounts)', value: 'jenkins' },
|
|
66
|
+
{ name: 'ALL PASSWORD-BASED ACCOUNTS', value: 'all' }
|
|
67
|
+
], validate: (v) => v.length > 0 || 'Select at least one target.' }]);
|
|
68
|
+
targets = chosen.targets.includes('all') ? ['cloud', 'npm', 'jenkins'] : chosen.targets;
|
|
69
|
+
}
|
|
70
|
+
const api = new cloud_api_service_1.CloudApiService();
|
|
71
|
+
await api.request('/auth/password-reset/request', { method: 'POST', body: JSON.stringify({ email }) });
|
|
72
|
+
console.log(chalk_1.default.green('✓ If that account exists, a 6-digit reset code was sent to its email.'));
|
|
73
|
+
const answers = await inquirer_1.default.prompt([
|
|
74
|
+
{ type: 'input', name: 'otp', message: 'Email OTP:', validate: (v) => /^\d{6}$/.test(v.trim()) || 'Enter the 6-digit code.' },
|
|
75
|
+
{ type: 'password', name: 'password', message: 'New password:', mask: '*', validate: (v) => v.length >= 10 || 'Use at least 10 characters.' },
|
|
76
|
+
{ type: 'password', name: 'confirm', message: 'Confirm new password:', mask: '*', validate: (v, a) => v === a.password || 'Passwords do not match.' }
|
|
77
|
+
]);
|
|
78
|
+
const result = await api.request('/auth/password-reset/verify', { method: 'POST', body: JSON.stringify({ email, otp: answers.otp.trim(), password: answers.password, targets }) });
|
|
79
|
+
console.table(result.results || []);
|
|
80
|
+
if (!result.ok)
|
|
81
|
+
throw new Error('One or more selected accounts could not be reset. Review the provider results.');
|
|
82
|
+
console.log(chalk_1.default.green('✓ Password reset completed.'));
|
|
83
|
+
if (targets.includes('cloud')) {
|
|
84
|
+
await api.login(email, answers.password);
|
|
85
|
+
console.log(chalk_1.default.gray(' Your CLI session was refreshed with the new IndraQ Cloud password.'));
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
exports.resetPasswordCloud = resetPasswordCloud;
|
|
89
|
+
const logoutCloud = () => {
|
|
90
|
+
const config = (0, cloud_config_1.getCloudConfig)();
|
|
91
|
+
(0, cloud_config_1.saveCloudConfig)({ apiUrl: config.apiUrl });
|
|
92
|
+
console.log(chalk_1.default.green('✓ Logged out.'));
|
|
93
|
+
};
|
|
94
|
+
exports.logoutCloud = logoutCloud;
|
|
95
|
+
const whoamiCloud = async () => console.log(await new cloud_api_service_1.CloudApiService().request('/auth/me'));
|
|
96
|
+
exports.whoamiCloud = whoamiCloud;
|
|
97
|
+
const listLogs = async (options = {}) => {
|
|
98
|
+
const rows = await new cloud_api_service_1.CloudApiService().request(`/logs${(0, listing_1.queryString)(options)}`);
|
|
99
|
+
console.table(rows.map((x) => ({ time: x.created_at, actor: x.actor_email, action: x.action, resource: x.resource_type, id: x.resource_id, reason: x.reason })));
|
|
100
|
+
};
|
|
101
|
+
exports.listLogs = listLogs;
|
|
102
|
+
const createServer = async (defaultEnvironment = 'production') => {
|
|
103
|
+
const validIpv4 = (value) => {
|
|
104
|
+
const parts = value.trim().split('.');
|
|
105
|
+
return parts.length === 4 && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) >= 0 && Number(part) <= 255);
|
|
106
|
+
};
|
|
107
|
+
const answer = await inquirer_1.default.prompt([
|
|
108
|
+
{ type: 'input', name: 'name', message: 'Server name:', validate: (v) => !!v.trim() || 'Required' },
|
|
109
|
+
{ type: 'input', name: 'environment', message: 'Environment:', default: defaultEnvironment },
|
|
110
|
+
{ type: 'input', name: 'ipv4', message: 'IPv4:', validate: (v) => validIpv4(v) || 'Enter a valid IPv4 address.' },
|
|
111
|
+
{ type: 'input', name: 'healthPath', message: 'Health path:', default: '/health' },
|
|
112
|
+
{ type: 'confirm', name: 'isBackup', message: 'Backup server?', default: false },
|
|
113
|
+
{ type: 'input', name: 'reason', message: 'Reason:', default: 'Create predefined deployment server', validate: (v) => v.trim().length >= 3 || 'Reason required' }
|
|
114
|
+
]);
|
|
115
|
+
await new cloud_api_service_1.CloudApiService().request('/servers', { method: 'POST', body: JSON.stringify(answer) });
|
|
116
|
+
console.log(chalk_1.default.green(`✓ Server ${answer.name} created.`));
|
|
117
|
+
};
|
|
118
|
+
exports.createServer = createServer;
|
|
119
|
+
const listServers = async () => console.table(await new cloud_api_service_1.CloudApiService().request('/servers'));
|
|
120
|
+
exports.listServers = listServers;
|
|
121
|
+
const syncProject = async () => {
|
|
122
|
+
const local = (0, project_metadata_1.getLocalProjectFile)();
|
|
123
|
+
if (!local)
|
|
124
|
+
throw new Error('No IndraQ project metadata found. For an existing application run `indraq project create`; for a new scaffold run `indraq init`.');
|
|
125
|
+
const api = new cloud_api_service_1.CloudApiService();
|
|
126
|
+
const root = (0, project_metadata_1.findProjectRoot)();
|
|
127
|
+
if (!root)
|
|
128
|
+
throw new Error('Project root could not be resolved.');
|
|
129
|
+
if (local.schemaVersion === 3) {
|
|
130
|
+
await api.request(`/projects/${local.projectId}`);
|
|
131
|
+
console.log(chalk_1.default.green(`✓ Project ${local.projectId} is linked to IndraQ Cloud.`));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const config = { ...local };
|
|
135
|
+
delete config.projectId;
|
|
136
|
+
const existingId = local.projectId;
|
|
137
|
+
if (existingId) {
|
|
138
|
+
await api.request(`/projects/${existingId}`, { method: 'PATCH', body: JSON.stringify({ name: local.name, config, reason: 'CLI project sync' }) });
|
|
139
|
+
(0, project_metadata_1.saveProjectReference)(root, existingId);
|
|
140
|
+
console.log(chalk_1.default.green(`✓ Project ${existingId} synced. Local project file now contains only the cloud project ID.`));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const remote = await api.request('/projects', { method: 'POST', body: JSON.stringify({ name: local.name, kind: local.kind, config, reason: 'Initial CLI project sync' }) });
|
|
144
|
+
(0, project_metadata_1.saveProjectReference)(root, remote.id);
|
|
145
|
+
console.log(chalk_1.default.green(`✓ Project created in IndraQ Cloud: ${remote.id}`));
|
|
146
|
+
console.log(chalk_1.default.gray(' .indraq/project.json now stores only the cloud project ID.'));
|
|
147
|
+
};
|
|
148
|
+
exports.syncProject = syncProject;
|
|
149
|
+
const readPackageJson = (root) => {
|
|
150
|
+
const path = (0, node_path_1.join)(root, 'package.json');
|
|
151
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
152
|
+
return null;
|
|
153
|
+
try {
|
|
154
|
+
return JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const hasPackage = (pkg, name) => !!pkg && (!!pkg.dependencies?.[name] || !!pkg.devDependencies?.[name]);
|
|
161
|
+
const inferFramework = (pkg) => {
|
|
162
|
+
if (hasPackage(pkg, 'expo'))
|
|
163
|
+
return 'expo';
|
|
164
|
+
if (hasPackage(pkg, 'react-native'))
|
|
165
|
+
return 'react-native';
|
|
166
|
+
if (hasPackage(pkg, 'express'))
|
|
167
|
+
return 'express';
|
|
168
|
+
if (hasPackage(pkg, 'next'))
|
|
169
|
+
return 'next';
|
|
170
|
+
if (hasPackage(pkg, 'vite'))
|
|
171
|
+
return 'vite';
|
|
172
|
+
if (hasPackage(pkg, 'react'))
|
|
173
|
+
return 'react';
|
|
174
|
+
return null;
|
|
175
|
+
};
|
|
176
|
+
const inferProjectKind = (root, pkg) => {
|
|
177
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, 'frontend', 'package.json')) && (0, node_fs_1.existsSync)((0, node_path_1.join)(root, 'backend', 'package.json')))
|
|
178
|
+
return 'fullstack';
|
|
179
|
+
const framework = inferFramework(pkg);
|
|
180
|
+
if (framework === 'expo' || framework === 'react-native')
|
|
181
|
+
return 'mobile';
|
|
182
|
+
if (framework === 'express')
|
|
183
|
+
return 'backend';
|
|
184
|
+
if (framework === 'vite' || framework === 'react' || framework === 'next')
|
|
185
|
+
return 'frontend';
|
|
186
|
+
return 'backend';
|
|
187
|
+
};
|
|
188
|
+
const inferNodeVersion = (pkg) => {
|
|
189
|
+
const engine = pkg?.engines?.node || '';
|
|
190
|
+
const match = engine.match(/(\d{2,})/);
|
|
191
|
+
return match?.[1] || process.versions.node.split('.')[0] || '22';
|
|
192
|
+
};
|
|
193
|
+
const projectSlug = (value) => {
|
|
194
|
+
const slug = value.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
195
|
+
return slug || 'app';
|
|
196
|
+
};
|
|
197
|
+
const positivePort = (value, fallback) => {
|
|
198
|
+
if (value === undefined || value === '')
|
|
199
|
+
return fallback;
|
|
200
|
+
const port = Number(value);
|
|
201
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
202
|
+
throw new Error('Port must be an integer from 1 to 65535.');
|
|
203
|
+
return port;
|
|
204
|
+
};
|
|
205
|
+
const ensureProjectCreateLogin = async () => {
|
|
206
|
+
const config = (0, cloud_config_1.getCloudConfig)();
|
|
207
|
+
if (!config?.token)
|
|
208
|
+
throw new Error('You must be logged in to IndraQ Cloud. Run `indraq login` first.');
|
|
209
|
+
try {
|
|
210
|
+
await new cloud_api_service_1.CloudApiService().request('/auth/me');
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
throw new Error('Your IndraQ Cloud session is invalid or expired. Run `indraq login` again.');
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
const chooseProjectKind = async (requested, detected) => {
|
|
217
|
+
const allowed = ['frontend', 'backend', 'fullstack', 'mobile'];
|
|
218
|
+
if (requested) {
|
|
219
|
+
const normalized = requested.trim().toLowerCase();
|
|
220
|
+
if (!allowed.includes(normalized))
|
|
221
|
+
throw new Error(`Unsupported project kind: ${requested}. Use frontend, backend, fullstack, or mobile.`);
|
|
222
|
+
return normalized;
|
|
223
|
+
}
|
|
224
|
+
return (await inquirer_1.default.prompt([{
|
|
225
|
+
type: 'list', name: 'kind', message: 'Project type:', default: detected,
|
|
226
|
+
choices: [
|
|
227
|
+
{ name: 'Frontend', value: 'frontend' },
|
|
228
|
+
{ name: 'Backend', value: 'backend' },
|
|
229
|
+
{ name: 'Frontend + Backend', value: 'fullstack' },
|
|
230
|
+
{ name: 'Mobile App', value: 'mobile' }
|
|
231
|
+
]
|
|
232
|
+
}])).kind;
|
|
233
|
+
};
|
|
234
|
+
const chooseFrontendFramework = async (requested, detected) => {
|
|
235
|
+
const allowed = ['vite', 'react', 'next', 'vanilla'];
|
|
236
|
+
if (requested) {
|
|
237
|
+
const normalized = requested.trim().toLowerCase();
|
|
238
|
+
if (!allowed.includes(normalized))
|
|
239
|
+
throw new Error(`Unsupported frontend framework: ${requested}. Use vite, react, next, or vanilla.`);
|
|
240
|
+
return normalized;
|
|
241
|
+
}
|
|
242
|
+
const fallback = detected && allowed.includes(detected)
|
|
243
|
+
? detected : 'vite';
|
|
244
|
+
return (await inquirer_1.default.prompt([{
|
|
245
|
+
type: 'list', name: 'framework', message: 'Frontend framework:', default: fallback, choices: allowed
|
|
246
|
+
}])).framework;
|
|
247
|
+
};
|
|
248
|
+
const chooseMobileFramework = async (requested, detected) => {
|
|
249
|
+
const allowed = ['expo', 'react-native'];
|
|
250
|
+
if (requested) {
|
|
251
|
+
const normalized = requested.trim().toLowerCase();
|
|
252
|
+
if (!allowed.includes(normalized))
|
|
253
|
+
throw new Error(`Unsupported mobile framework: ${requested}. Use expo or react-native.`);
|
|
254
|
+
return normalized;
|
|
255
|
+
}
|
|
256
|
+
const fallback = detected === 'react-native' ? 'react-native' : 'expo';
|
|
257
|
+
return (await inquirer_1.default.prompt([{
|
|
258
|
+
type: 'list', name: 'framework', message: 'Mobile framework:', default: fallback, choices: allowed
|
|
259
|
+
}])).framework;
|
|
260
|
+
};
|
|
261
|
+
const safeRelativePath = (root, value) => {
|
|
262
|
+
const trimmed = value.trim();
|
|
263
|
+
if (!trimmed || trimmed.startsWith('/') || /^[A-Za-z]:[\\/]/.test(trimmed) || trimmed.split(/[\\/]+/).includes('..'))
|
|
264
|
+
return false;
|
|
265
|
+
const target = (0, node_path_1.resolve)(root, trimmed);
|
|
266
|
+
return (target === root || target.startsWith(`${root}${process.platform === 'win32' ? '\\' : '/'}`)) && (0, node_fs_1.existsSync)(target);
|
|
267
|
+
};
|
|
268
|
+
const buildExistingFolderMetadata = async (root, name, kind, options, rootPackage) => {
|
|
269
|
+
const nodeVersion = options.nodeVersion?.trim() || inferNodeVersion(rootPackage);
|
|
270
|
+
const slug = projectSlug(options.image?.trim() || name);
|
|
271
|
+
const detected = inferFramework(rootPackage);
|
|
272
|
+
if (kind === 'mobile') {
|
|
273
|
+
const framework = await chooseMobileFramework(options.framework, detected);
|
|
274
|
+
return { schemaVersion: 1, name, kind, framework, nodeVersion, appPort: 0, containerPort: 0, healthEndpoint: '', imageName: slug, createdAt: new Date().toISOString() };
|
|
275
|
+
}
|
|
276
|
+
if (kind === 'backend') {
|
|
277
|
+
if (options.framework && options.framework.trim().toLowerCase() !== 'express')
|
|
278
|
+
throw new Error('The current IndraQ backend metadata model supports framework=express.');
|
|
279
|
+
let port = positivePort(options.port, 3000);
|
|
280
|
+
if (!options.port)
|
|
281
|
+
port = (await inquirer_1.default.prompt([{ type: 'number', name: 'port', message: 'Application/container port:', default: port, validate: (v) => Number.isInteger(v) && v >= 1 && v <= 65535 || 'Use a port from 1-65535.' }])).port;
|
|
282
|
+
const healthEndpoint = options.health?.trim() || (await inquirer_1.default.prompt([{ type: 'input', name: 'health', message: 'Application health endpoint (metadata only):', default: '/health', validate: (v) => v.trim().startsWith('/') || 'Path must start with /.' }])).health.trim();
|
|
283
|
+
return { schemaVersion: 2, name, kind, framework: 'express', nodeVersion, appPort: port, containerPort: port, healthEndpoint, imageName: slug, createdAt: new Date().toISOString() };
|
|
284
|
+
}
|
|
285
|
+
if (kind === 'frontend') {
|
|
286
|
+
const framework = await chooseFrontendFramework(options.framework, detected);
|
|
287
|
+
const defaultPort = framework === 'next' ? 3000 : 80;
|
|
288
|
+
const port = positivePort(options.port, defaultPort);
|
|
289
|
+
const healthEndpoint = options.health?.trim() || '/';
|
|
290
|
+
const buildOutput = framework === 'vite' || framework === 'react' ? 'dist' : undefined;
|
|
291
|
+
return { schemaVersion: 2, name, kind, framework, nodeVersion, appPort: port, containerPort: port, healthEndpoint, imageName: slug, createdAt: new Date().toISOString(), ...(buildOutput ? { buildOutput } : {}) };
|
|
292
|
+
}
|
|
293
|
+
const defaultFrontendPath = (0, node_fs_1.existsSync)((0, node_path_1.join)(root, 'frontend')) ? 'frontend' : 'client';
|
|
294
|
+
const defaultBackendPath = (0, node_fs_1.existsSync)((0, node_path_1.join)(root, 'backend')) ? 'backend' : 'server';
|
|
295
|
+
const paths = await inquirer_1.default.prompt([
|
|
296
|
+
{ type: 'input', name: 'frontendPath', message: 'Frontend path relative to this folder:', default: defaultFrontendPath, validate: (v) => safeRelativePath(root, v) || 'Enter an existing path inside the current project.' },
|
|
297
|
+
{ type: 'input', name: 'backendPath', message: 'Backend path relative to this folder:', default: defaultBackendPath, validate: (v) => safeRelativePath(root, v) || 'Enter an existing path inside the current project.' }
|
|
298
|
+
]);
|
|
299
|
+
const frontendPackage = readPackageJson((0, node_path_1.join)(root, paths.frontendPath));
|
|
300
|
+
const frontendFramework = await chooseFrontendFramework(options.framework, inferFramework(frontendPackage));
|
|
301
|
+
let backendPort = positivePort(options.port, 3000);
|
|
302
|
+
if (!options.port)
|
|
303
|
+
backendPort = (await inquirer_1.default.prompt([{ type: 'number', name: 'port', message: 'Backend application/container port:', default: backendPort, validate: (v) => Number.isInteger(v) && v >= 1 && v <= 65535 || 'Use a port from 1-65535.' }])).port;
|
|
304
|
+
const backendHealth = options.health?.trim() || (await inquirer_1.default.prompt([{ type: 'input', name: 'health', message: 'Backend health endpoint (metadata only):', default: '/health', validate: (v) => v.trim().startsWith('/') || 'Path must start with /.' }])).health.trim();
|
|
305
|
+
const frontendPort = frontendFramework === 'next' ? 3000 : 80;
|
|
306
|
+
const frontendBuildOutput = frontendFramework === 'vite' || frontendFramework === 'react' ? 'dist' : undefined;
|
|
307
|
+
const components = [
|
|
308
|
+
{ name: `${name}-frontend`, kind: 'frontend', path: paths.frontendPath.trim(), framework: frontendFramework, appPort: frontendPort, containerPort: frontendPort, healthEndpoint: '/', imageName: `${slug}-frontend`, ...(frontendBuildOutput ? { buildOutput: frontendBuildOutput } : {}) },
|
|
309
|
+
{ name: `${name}-backend`, kind: 'backend', path: paths.backendPath.trim(), framework: 'express', appPort: backendPort, containerPort: backendPort, healthEndpoint: backendHealth, imageName: `${slug}-backend` }
|
|
310
|
+
];
|
|
311
|
+
return { schemaVersion: 2, name, kind, framework: frontendFramework, nodeVersion, appPort: frontendPort, containerPort: frontendPort, healthEndpoint: '/', imageName: slug, createdAt: new Date().toISOString(), components, ...(frontendBuildOutput ? { buildOutput: frontendBuildOutput } : {}) };
|
|
312
|
+
};
|
|
313
|
+
const createProject = async (requestedName, options) => {
|
|
314
|
+
await ensureProjectCreateLogin();
|
|
315
|
+
const root = (0, node_path_1.resolve)(process.cwd());
|
|
316
|
+
const exactLocalProjectFile = (0, node_path_1.join)(root, '.indraq', 'project.json');
|
|
317
|
+
const existingLocal = (0, node_fs_1.existsSync)(exactLocalProjectFile) ? (0, project_metadata_1.getLocalProjectFile)(root) : null;
|
|
318
|
+
if (existingLocal) {
|
|
319
|
+
if (existingLocal.schemaVersion === 3) {
|
|
320
|
+
const existingRemote = await new cloud_api_service_1.CloudApiService().request(`/projects/${existingLocal.projectId}`);
|
|
321
|
+
console.log(chalk_1.default.green(`✓ This folder is already linked to ${existingRemote.name} (${existingRemote.id}).`));
|
|
322
|
+
console.log(chalk_1.default.gray(' Use `indraq project status`, `indraq project sync`, or `indraq project link` instead of creating a duplicate project.'));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
throw new Error('This folder already contains IndraQ project metadata. Run `indraq project sync` to create/link its Cloud project.');
|
|
326
|
+
}
|
|
327
|
+
const rootPackage = readPackageJson(root);
|
|
328
|
+
const inferredName = rootPackage?.name?.trim() || (0, node_path_1.basename)(root);
|
|
329
|
+
const name = (requestedName?.trim() || (await inquirer_1.default.prompt([{
|
|
330
|
+
type: 'input', name: 'name', message: 'Cloud project name:', default: inferredName, validate: (v) => !!v.trim() || 'Project name is required.'
|
|
331
|
+
}])).name.trim());
|
|
332
|
+
if (!name)
|
|
333
|
+
throw new Error('Project name is required.');
|
|
334
|
+
const api = new cloud_api_service_1.CloudApiService();
|
|
335
|
+
const accessible = await api.request('/projects');
|
|
336
|
+
const sameName = accessible.find((project) => project.name.trim().toLowerCase() === name.toLowerCase());
|
|
337
|
+
if (sameName) {
|
|
338
|
+
const linkExisting = options.yes || (await inquirer_1.default.prompt([{
|
|
339
|
+
type: 'confirm', name: 'ok', message: `A Cloud project named "${sameName.name}" already exists and is accessible. Link this folder to it instead?`, default: true
|
|
340
|
+
}])).ok;
|
|
341
|
+
if (linkExisting) {
|
|
342
|
+
(0, project_metadata_1.saveProjectReference)(root, sameName.id);
|
|
343
|
+
console.log(chalk_1.default.green(`✓ Linked this folder to existing Cloud project ${sameName.name} (${sameName.id}).`));
|
|
344
|
+
console.log(chalk_1.default.gray(' Only .indraq/project.json was created. No application, package, Docker, AWS, NPM, Jenkins, or GHCR files/resources were changed.'));
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const kind = await chooseProjectKind(options.kind, inferProjectKind(root, rootPackage));
|
|
349
|
+
const metadata = await buildExistingFolderMetadata(root, name, kind, options, rootPackage);
|
|
350
|
+
const reason = options.reason?.trim() || (options.yes ? 'Register existing folder as IndraQ Cloud project' : (await inquirer_1.default.prompt([{
|
|
351
|
+
type: 'input', name: 'reason', message: 'Audit reason:', default: 'Register existing folder as IndraQ Cloud project', validate: (v) => v.trim().length >= 3 || 'Reason is required.'
|
|
352
|
+
}])).reason.trim());
|
|
353
|
+
const remote = await api.request('/projects', {
|
|
354
|
+
method: 'POST', body: JSON.stringify({ name, kind, config: metadata, reason })
|
|
355
|
+
});
|
|
356
|
+
(0, project_metadata_1.saveProjectReference)(root, remote.id);
|
|
357
|
+
console.log(chalk_1.default.green(`✓ Cloud project created: ${remote.name} (${remote.id}).`));
|
|
358
|
+
console.log(chalk_1.default.green('✓ Current folder linked to IndraQ Cloud.'));
|
|
359
|
+
console.log(chalk_1.default.gray(' Only .indraq/project.json was created. Your existing application files were not modified.'));
|
|
360
|
+
console.log(chalk_1.default.gray(' No npm install, Docker generation, AWS, NPM, Jenkins, GHCR, prebuild, or deployment action was run.'));
|
|
361
|
+
console.log(chalk_1.default.gray(' You can now run individual commands such as `indraq dockerfile create`, `indraq jenkins create-deployment`, `indraq dns create`, `indraq proxy create`, or `indraq prebuild`.'));
|
|
362
|
+
};
|
|
363
|
+
exports.createProject = createProject;
|
|
364
|
+
const projectStatus = async (environment) => {
|
|
365
|
+
const project = await (0, project_cloud_service_1.getCloudProject)();
|
|
366
|
+
const resources = await (0, project_cloud_service_1.listProjectResources)(environment);
|
|
367
|
+
console.log(chalk_1.default.blue.bold(`\n${project.name} — IndraQ Cloud project`));
|
|
368
|
+
console.log(chalk_1.default.gray(`Project ID: ${project.id}`));
|
|
369
|
+
console.log(chalk_1.default.gray(`Access: ${project.effective_access === 'write' ? 'WRITE' : 'VIEW ONLY'}`));
|
|
370
|
+
if (!resources.length) {
|
|
371
|
+
console.log(chalk_1.default.yellow(`⚠ No${environment ? ` ${environment}` : ''} infrastructure resources are associated. Run \`indraq prebuild${environment ? ` --env ${environment}` : ''}\`.`));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
console.table(resources.map((item) => ({ environment: item.environment, resource: item.resource_type, provider: item.provider, externalId: item.external_id || '' })));
|
|
375
|
+
};
|
|
376
|
+
exports.projectStatus = projectStatus;
|
|
377
|
+
const chooseCloudProject = async (message = 'Project:', options = {}) => {
|
|
378
|
+
const projects = await new cloud_api_service_1.CloudApiService().request(`/projects${(0, listing_1.queryString)(options)}`);
|
|
379
|
+
if (!projects.length)
|
|
380
|
+
throw new Error('No matching IndraQ Cloud projects were found.');
|
|
381
|
+
const id = (await inquirer_1.default.prompt([{ type: 'list', name: 'id', message, pageSize: 15, choices: projects.map((p) => ({ name: `${p.name} — ${p.kind} — ${(p.effective_access || 'view').toUpperCase()}${p.creator_email ? ` — ${p.creator_email}` : ''}`, value: p.id })) }])).id;
|
|
382
|
+
return projects.find((p) => p.id === id);
|
|
383
|
+
};
|
|
384
|
+
const listProjects = async (options) => {
|
|
385
|
+
const projects = await new cloud_api_service_1.CloudApiService().request(`/projects${(0, listing_1.queryString)(options)}`);
|
|
386
|
+
console.table(projects.map((p) => ({ name: p.name, kind: p.kind, access: (p.effective_access || 'view').toUpperCase(), creator: p.creator_email || '', id: p.id })));
|
|
387
|
+
};
|
|
388
|
+
exports.listProjects = listProjects;
|
|
389
|
+
const linkProject = async (options = {}) => {
|
|
390
|
+
const project = await chooseCloudProject('Cloud project to link to this folder:', options);
|
|
391
|
+
const root = (0, node_path_1.resolve)(process.cwd());
|
|
392
|
+
let existing = null;
|
|
393
|
+
try {
|
|
394
|
+
existing = (0, project_metadata_1.getLocalProjectFile)(root);
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
console.log(chalk_1.default.yellow(`⚠ Existing .indraq/project.json is invalid and can be repaired: ${error instanceof Error ? error.message : String(error)}`));
|
|
398
|
+
}
|
|
399
|
+
if (existing?.schemaVersion === 3 && existing.projectId === project.id) {
|
|
400
|
+
console.log(chalk_1.default.green(`✓ This folder is already linked to ${project.name} (${project.id}).`));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (existing) {
|
|
404
|
+
const ok = (await inquirer_1.default.prompt([{ type: 'confirm', name: 'ok', message: 'This folder already has an IndraQ project reference. Replace only the .indraq/project.json link?', default: false }])).ok;
|
|
405
|
+
if (!ok)
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
(0, project_metadata_1.saveProjectReference)(root, project.id);
|
|
409
|
+
console.log(chalk_1.default.green(`✓ Linked ${root} to ${project.name} (${project.id}).`));
|
|
410
|
+
console.log(chalk_1.default.gray(' Only .indraq/project.json was created/replaced; your application files were not modified.'));
|
|
411
|
+
};
|
|
412
|
+
exports.linkProject = linkProject;
|
|
413
|
+
const addProjectUser = async () => {
|
|
414
|
+
const api = new cloud_api_service_1.CloudApiService();
|
|
415
|
+
const project = await chooseCloudProject('Project to grant access:');
|
|
416
|
+
const users = (await api.request('/users?all=true')).filter((u) => u.is_active);
|
|
417
|
+
if (!users.length)
|
|
418
|
+
throw new Error('No active users are available to add.');
|
|
419
|
+
const userId = (await inquirer_1.default.prompt([{ type: 'list', name: 'userId', message: 'User to add:', choices: users.map((u) => ({ name: `${u.email} — ${u.name} (${u.role})`, value: u.id })) }])).userId;
|
|
420
|
+
const accessLevel = (await inquirer_1.default.prompt([{ type: 'list', name: 'accessLevel', message: 'Project permission:', choices: [
|
|
421
|
+
{ name: 'VIEW — inspect/link project, no infrastructure changes', value: 'view' },
|
|
422
|
+
{ name: 'WRITE — run prebuild/deployment and manage project resources', value: 'write' }
|
|
423
|
+
], default: 'write' }])).accessLevel;
|
|
424
|
+
const reason = (await inquirer_1.default.prompt([{ type: 'input', name: 'reason', message: 'Reason:', default: 'Grant project access', validate: (v) => v.trim().length >= 3 || 'Reason is required.' }])).reason.trim();
|
|
425
|
+
await api.request(`/projects/${project.id}/members`, { method: 'POST', body: JSON.stringify({ userId, accessLevel, reason }) });
|
|
426
|
+
const user = users.find((u) => u.id === userId);
|
|
427
|
+
console.log(chalk_1.default.green(`✓ ${user.email} now has access to ${project.name}.`));
|
|
428
|
+
};
|
|
429
|
+
exports.addProjectUser = addProjectUser;
|
|
430
|
+
const deleteProjectUser = async () => {
|
|
431
|
+
const api = new cloud_api_service_1.CloudApiService();
|
|
432
|
+
const project = await chooseCloudProject('Project to remove access from:');
|
|
433
|
+
const members = await api.request(`/projects/${project.id}/members`);
|
|
434
|
+
const removable = members.filter((m) => m.role !== 'owner');
|
|
435
|
+
if (!removable.length)
|
|
436
|
+
throw new Error('This project has no removable members. Project owners cannot be removed through this command.');
|
|
437
|
+
const userId = (await inquirer_1.default.prompt([{ type: 'list', name: 'userId', message: 'User to remove:', choices: removable.map((m) => ({ name: `${m.email} — ${m.name}`, value: m.user_id })) }])).userId;
|
|
438
|
+
const reason = (await inquirer_1.default.prompt([{ type: 'input', name: 'reason', message: 'Reason:', default: 'Revoke project access', validate: (v) => v.trim().length >= 3 || 'Reason is required.' }])).reason.trim();
|
|
439
|
+
await api.request(`/projects/${project.id}/members/${userId}`, { method: 'DELETE', body: JSON.stringify({ reason }) });
|
|
440
|
+
console.log(chalk_1.default.green('✓ Project access removed.'));
|
|
441
|
+
};
|
|
442
|
+
exports.deleteProjectUser = deleteProjectUser;
|
|
443
|
+
//# sourceMappingURL=cloud.command.js.map
|