indraq_cli 1.9.1 → 2.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/LICENSE +21 -21
- package/README.md +1043 -1517
- package/cloud-api/.env +6 -0
- package/cloud-api/.env.example +14 -14
- package/cloud-api/README.md +145 -141
- package/cloud-api/package.json +29 -29
- package/cloud-api/sql/001_init.sql +8 -8
- package/cloud-api/sql/002_user_providers_and_project_resources.sql +40 -40
- package/cloud-api/sql/003_environment_infrastructure.sql +19 -19
- package/cloud-api/sql/004_managed_aws_credentials.sql +20 -20
- package/cloud-api/sql/005_project_members_and_resource_grants.sql +22 -22
- package/cloud-api/sql/006_catalog_identity_password_security.sql +96 -96
- package/cloud-api/sql/007_aws_access_key_rotation.sql +16 -16
- package/cloud-api/src/index.ts +1329 -1254
- package/cloud-api/tsconfig.json +18 -18
- package/dist/cli/help-content.d.ts.map +1 -1
- package/dist/cli/help-content.js +254 -251
- package/dist/cli/help-content.js.map +1 -1
- package/dist/modules/access/commands/access.command.d.ts +19 -0
- package/dist/modules/access/commands/access.command.d.ts.map +1 -1
- package/dist/modules/access/commands/access.command.js +57 -46
- package/dist/modules/access/commands/access.command.js.map +1 -1
- package/dist/modules/cloud/commands/cloud.command.d.ts.map +1 -1
- package/dist/modules/cloud/commands/cloud.command.js +18 -2
- package/dist/modules/cloud/commands/cloud.command.js.map +1 -1
- package/dist/modules/cloud/config/cloud-config.d.ts +1 -1
- package/dist/modules/cloud/config/cloud-config.js +1 -1
- package/dist/modules/cloud/services/cloud-jenkins.service.d.ts +13 -1
- package/dist/modules/cloud/services/cloud-jenkins.service.d.ts.map +1 -1
- package/dist/modules/cloud/services/cloud-jenkins.service.js +173 -77
- package/dist/modules/cloud/services/cloud-jenkins.service.js.map +1 -1
- package/dist/modules/cloud/services/managed-aws-credential.service.d.ts.map +1 -1
- package/dist/modules/cloud/services/managed-aws-credential.service.js +11 -4
- package/dist/modules/cloud/services/managed-aws-credential.service.js.map +1 -1
- package/dist/modules/jenkins/commands/admin.command.d.ts +3 -0
- package/dist/modules/jenkins/commands/admin.command.d.ts.map +1 -1
- package/dist/modules/jenkins/commands/admin.command.js +91 -1
- package/dist/modules/jenkins/commands/admin.command.js.map +1 -1
- package/dist/modules/jenkins/index.d.ts.map +1 -1
- package/dist/modules/jenkins/index.js +3 -0
- package/dist/modules/jenkins/index.js.map +1 -1
- package/dist/modules/scaffold/services/docker-template.service.js +95 -95
- package/dist/modules/users/commands/user.command.d.ts.map +1 -1
- package/dist/modules/users/commands/user.command.js +290 -44
- package/dist/modules/users/commands/user.command.js.map +1 -1
- package/dist/modules/users/providers/aws-user.provider.d.ts.map +1 -1
- package/dist/modules/users/providers/aws-user.provider.js +9 -2
- package/dist/modules/users/providers/aws-user.provider.js.map +1 -1
- package/dist/modules/users/providers/jenkins-user.provider.d.ts +11 -4
- package/dist/modules/users/providers/jenkins-user.provider.d.ts.map +1 -1
- package/dist/modules/users/providers/jenkins-user.provider.js +174 -72
- package/dist/modules/users/providers/jenkins-user.provider.js.map +1 -1
- package/dist/modules/users/providers/npm-user.provider.d.ts.map +1 -1
- package/dist/modules/users/providers/npm-user.provider.js +3 -1
- package/dist/modules/users/providers/npm-user.provider.js.map +1 -1
- package/dist/shared/config/config-files.js +7 -7
- package/docs/ARCHITECTURE.md +29 -29
- package/docs/CREATE-DEPLOYMENT-reference.groovy +1105 -1105
- package/package.json +77 -76
- package/templates/jenkins/CREATE-DEPLOYMENT.groovy +1105 -1105
- package/templates/jenkins/Jenkinsfile-Mobile-App +759 -759
- package/cloud-api/.dockerignore +0 -18
- package/cloud-api/Dockerfile +0 -19
- package/cloud-api/package-lock.json +0 -2250
package/cloud-api/.env
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
PORT=4010
|
|
2
|
+
DATABASE_URL=postgresql://harry:Staykool%401@192.168.10.101:23561/indraq_cloud
|
|
3
|
+
JWT_SECRET=8f3c2a9d4e6b7f00112233445566778899aabbccddeeff001122334455667788
|
|
4
|
+
MASTER_KEY=8f3c2a9d4e6b7f00112233445566778899aabbccddeeff001122334455667788
|
|
5
|
+
BOOTSTRAP_ADMIN_EMAIL=harkaran@indraq.com
|
|
6
|
+
BOOTSTRAP_ADMIN_PASSWORD=Saini@harkaran-1
|
package/cloud-api/.env.example
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
PORT=4010
|
|
2
|
-
DATABASE_URL=postgresql://indraq:change-me@127.0.0.1:5432/indraq_cloud
|
|
3
|
-
JWT_SECRET=replace-with-64-random-characters
|
|
4
|
-
MASTER_KEY=replace-with-64-hex-characters
|
|
5
|
-
BOOTSTRAP_ADMIN_EMAIL=admin@example.com
|
|
6
|
-
BOOTSTRAP_ADMIN_PASSWORD=change-this-immediately
|
|
7
|
-
|
|
8
|
-
# Password-reset email (required for `indraq password reset`)
|
|
9
|
-
SMTP_HOST=smtp.example.com
|
|
10
|
-
SMTP_PORT=587
|
|
11
|
-
SMTP_SECURE=false
|
|
12
|
-
SMTP_USER=
|
|
13
|
-
SMTP_PASS=
|
|
14
|
-
SMTP_FROM=IndraQ <no-reply@example.com>
|
|
1
|
+
PORT=4010
|
|
2
|
+
DATABASE_URL=postgresql://indraq:change-me@127.0.0.1:5432/indraq_cloud
|
|
3
|
+
JWT_SECRET=replace-with-64-random-characters
|
|
4
|
+
MASTER_KEY=replace-with-64-hex-characters
|
|
5
|
+
BOOTSTRAP_ADMIN_EMAIL=admin@example.com
|
|
6
|
+
BOOTSTRAP_ADMIN_PASSWORD=change-this-immediately
|
|
7
|
+
|
|
8
|
+
# Password-reset email (required for `indraq password reset`)
|
|
9
|
+
SMTP_HOST=smtp.example.com
|
|
10
|
+
SMTP_PORT=587
|
|
11
|
+
SMTP_SECURE=false
|
|
12
|
+
SMTP_USER=
|
|
13
|
+
SMTP_PASS=
|
|
14
|
+
SMTP_FROM=IndraQ <no-reply@example.com>
|
package/cloud-api/README.md
CHANGED
|
@@ -1,141 +1,145 @@
|
|
|
1
|
-
# IndraQ Cloud API
|
|
2
|
-
|
|
3
|
-
IndraQ Cloud API is the control plane used by IndraQ CLI. It provides authentication, RBAC, projects, shared environments, encrypted user-scoped provider credentials, managed AWS IAM credential recovery/rotation, first-login password enforcement, email-OTP password reset, project infrastructure associations, automatic PostgreSQL migrations, and audit logs.
|
|
4
|
-
|
|
5
|
-
## Requirements
|
|
6
|
-
|
|
7
|
-
- Node.js 22+
|
|
8
|
-
- PostgreSQL
|
|
9
|
-
|
|
10
|
-
## Configure
|
|
11
|
-
|
|
12
|
-
```powershell
|
|
13
|
-
npm install
|
|
14
|
-
Copy-Item .env.example .env
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Required environment values:
|
|
18
|
-
|
|
19
|
-
```env
|
|
20
|
-
PORT=4010
|
|
21
|
-
DATABASE_URL=postgresql://indraq:change-me@127.0.0.1:5432/indraq_cloud
|
|
22
|
-
JWT_SECRET=replace-with-at-least-32-random-characters
|
|
23
|
-
MASTER_KEY=replace-with-exactly-64-hex-characters
|
|
24
|
-
BOOTSTRAP_ADMIN_EMAIL=admin@example.com
|
|
25
|
-
BOOTSTRAP_ADMIN_PASSWORD=use-a-strong-password
|
|
26
|
-
|
|
27
|
-
# Required for `indraq password reset`
|
|
28
|
-
SMTP_HOST=smtp.example.com
|
|
29
|
-
SMTP_PORT=587
|
|
30
|
-
SMTP_SECURE=false
|
|
31
|
-
SMTP_USER=
|
|
32
|
-
SMTP_PASS=
|
|
33
|
-
SMTP_FROM=IndraQ <no-reply@example.com>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Rules:
|
|
37
|
-
|
|
38
|
-
- `JWT_SECRET` must be at least 32 characters.
|
|
39
|
-
- `MASTER_KEY` must be exactly 64 hexadecimal characters (32 bytes).
|
|
40
|
-
- `BOOTSTRAP_ADMIN_PASSWORD` must be at least 12 characters.
|
|
41
|
-
|
|
42
|
-
Generate a master key:
|
|
43
|
-
|
|
44
|
-
```powershell
|
|
45
|
-
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Start
|
|
49
|
-
|
|
50
|
-
Development:
|
|
51
|
-
|
|
52
|
-
```powershell
|
|
53
|
-
npm run build
|
|
54
|
-
npm run dev
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Production:
|
|
58
|
-
|
|
59
|
-
```powershell
|
|
60
|
-
npm run build
|
|
61
|
-
npm start
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Numbered migrations in `sql/` run automatically at startup.
|
|
65
|
-
|
|
66
|
-
## Bootstrap admin
|
|
67
|
-
|
|
68
|
-
When no admin exists, `BOOTSTRAP_ADMIN_EMAIL` and `BOOTSTRAP_ADMIN_PASSWORD` create the first administrator.
|
|
69
|
-
|
|
70
|
-
Keep `MASTER_KEY` stable. Existing encrypted provider credentials cannot be resolved with a different key.
|
|
71
|
-
|
|
72
|
-
## RBAC
|
|
73
|
-
|
|
74
|
-
Roles:
|
|
75
|
-
|
|
76
|
-
- `user`
|
|
77
|
-
- `manager`
|
|
78
|
-
- `admin`
|
|
79
|
-
|
|
80
|
-
Managers can create/manage normal users. Only admins can create managers/admins, change user roles, and manage shared environments. Managers/admins can view audit logs.
|
|
81
|
-
|
|
82
|
-
Authentication middleware rechecks the current database user state/role for protected requests rather than relying only on a stale role claim.
|
|
83
|
-
|
|
84
|
-
## User-scoped provider credentials
|
|
85
|
-
|
|
86
|
-
Reusable provider credentials are stored in `user_integrations` and keyed by `(user_id, provider)`.
|
|
87
|
-
|
|
88
|
-
Supported provider profiles:
|
|
89
|
-
|
|
90
|
-
- AWS
|
|
91
|
-
- NPM
|
|
92
|
-
- Jenkins Development
|
|
93
|
-
- Jenkins Production
|
|
94
|
-
- GHCR
|
|
95
|
-
|
|
96
|
-
Secrets use AES-256-GCM because provider APIs need the original credential later. Account passwords use bcrypt.
|
|
97
|
-
|
|
98
|
-
Users cannot resolve another user's provider credentials through the normal API. Provider resolution is audited and responses use `Cache-Control: no-store`.
|
|
99
|
-
|
|
100
|
-
## Managed AWS IAM credentials
|
|
101
|
-
|
|
102
|
-
When unified user creation generates an AWS access key, AWS returns the secret only at creation time. IndraQ can store that generated secret encrypted in `managed_aws_credentials` for the target Cloud user.
|
|
103
|
-
|
|
104
|
-
The target logged-in user can later resolve their own stored AWS credential. Users can rotate their own IndraQ-managed AWS key; managers/admins can rotate keys for users they are authorized to manage. Rotation creates the replacement key, stores it encrypted, updates the target provider credential, then deletes the previous managed key. Administrative creation/deletion/rotation and user resolution are audited.
|
|
105
|
-
|
|
106
|
-
## Password lifecycle
|
|
107
|
-
|
|
108
|
-
Users created through the management API receive `must_change_password=true`. The first successful login must be followed by `/auth/first-login-password` before normal protected operations are allowed. Managed NPM/Jenkins identities created through the unified workflow can be rotated to the new password as part of that first-login change.
|
|
109
|
-
|
|
110
|
-
Self-service password recovery uses `/auth/password-reset/request` and `/auth/password-reset/verify`. OTP values are bcrypt-hashed in PostgreSQL, expire after 10 minutes, are rate-limited, and have an attempt limit. SMTP must be configured for delivery. AWS is not a password-reset target because IndraQ AWS users are CLI/API access-key users.
|
|
111
|
-
|
|
112
|
-
## Shared environments
|
|
113
|
-
|
|
114
|
-
`environments` are admin-managed reusable server targets containing values such as:
|
|
115
|
-
|
|
116
|
-
- name
|
|
117
|
-
- description
|
|
118
|
-
- IPv4 address
|
|
119
|
-
- region
|
|
120
|
-
|
|
121
|
-
Environment names are arbitrary. Environments are reusable address targets only; Route53 health-check settings are chosen per DNS record. PRIMARY/SECONDARY roles are assigned by a project DNS routing configuration, not stored permanently on the environment.
|
|
122
|
-
|
|
123
|
-
## Projects and project resources
|
|
124
|
-
|
|
125
|
-
Projects are stored separately from reusable user credentials.
|
|
126
|
-
|
|
127
|
-
`project_resources` stores project-specific associations such as:
|
|
128
|
-
|
|
129
|
-
- GHCR image
|
|
130
|
-
- Jenkins job
|
|
131
|
-
- Route53 record/health-check metadata
|
|
132
|
-
- NPM proxy
|
|
133
|
-
- environment association
|
|
134
|
-
|
|
135
|
-
`project_members` controls
|
|
136
|
-
|
|
137
|
-
`project_resource_grants` records service/resource-level access
|
|
138
|
-
|
|
139
|
-
##
|
|
140
|
-
|
|
141
|
-
|
|
1
|
+
# IndraQ Cloud API
|
|
2
|
+
|
|
3
|
+
IndraQ Cloud API is the control plane used by IndraQ CLI. It provides authentication, RBAC, projects, shared environments, encrypted user-scoped provider credentials, managed AWS IAM credential recovery/rotation, first-login password enforcement, email-OTP password reset, project infrastructure associations, automatic PostgreSQL migrations, and audit logs.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 22+
|
|
8
|
+
- PostgreSQL
|
|
9
|
+
|
|
10
|
+
## Configure
|
|
11
|
+
|
|
12
|
+
```powershell
|
|
13
|
+
npm install
|
|
14
|
+
Copy-Item .env.example .env
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Required environment values:
|
|
18
|
+
|
|
19
|
+
```env
|
|
20
|
+
PORT=4010
|
|
21
|
+
DATABASE_URL=postgresql://indraq:change-me@127.0.0.1:5432/indraq_cloud
|
|
22
|
+
JWT_SECRET=replace-with-at-least-32-random-characters
|
|
23
|
+
MASTER_KEY=replace-with-exactly-64-hex-characters
|
|
24
|
+
BOOTSTRAP_ADMIN_EMAIL=admin@example.com
|
|
25
|
+
BOOTSTRAP_ADMIN_PASSWORD=use-a-strong-password
|
|
26
|
+
|
|
27
|
+
# Required for `indraq password reset`
|
|
28
|
+
SMTP_HOST=smtp.example.com
|
|
29
|
+
SMTP_PORT=587
|
|
30
|
+
SMTP_SECURE=false
|
|
31
|
+
SMTP_USER=
|
|
32
|
+
SMTP_PASS=
|
|
33
|
+
SMTP_FROM=IndraQ <no-reply@example.com>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Rules:
|
|
37
|
+
|
|
38
|
+
- `JWT_SECRET` must be at least 32 characters.
|
|
39
|
+
- `MASTER_KEY` must be exactly 64 hexadecimal characters (32 bytes).
|
|
40
|
+
- `BOOTSTRAP_ADMIN_PASSWORD` must be at least 12 characters.
|
|
41
|
+
|
|
42
|
+
Generate a master key:
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Start
|
|
49
|
+
|
|
50
|
+
Development:
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
npm run build
|
|
54
|
+
npm run dev
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Production:
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
npm run build
|
|
61
|
+
npm start
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Numbered migrations in `sql/` run automatically at startup.
|
|
65
|
+
|
|
66
|
+
## Bootstrap admin
|
|
67
|
+
|
|
68
|
+
When no admin exists, `BOOTSTRAP_ADMIN_EMAIL` and `BOOTSTRAP_ADMIN_PASSWORD` create the first administrator.
|
|
69
|
+
|
|
70
|
+
Keep `MASTER_KEY` stable. Existing encrypted provider credentials cannot be resolved with a different key.
|
|
71
|
+
|
|
72
|
+
## RBAC
|
|
73
|
+
|
|
74
|
+
Roles:
|
|
75
|
+
|
|
76
|
+
- `user`
|
|
77
|
+
- `manager`
|
|
78
|
+
- `admin`
|
|
79
|
+
|
|
80
|
+
Managers can create/manage normal users. Only admins can create managers/admins, change user roles, and manage shared environments. Managers/admins can view audit logs.
|
|
81
|
+
|
|
82
|
+
Authentication middleware rechecks the current database user state/role for protected requests rather than relying only on a stale role claim.
|
|
83
|
+
|
|
84
|
+
## User-scoped provider credentials
|
|
85
|
+
|
|
86
|
+
Reusable provider credentials are stored in `user_integrations` and keyed by `(user_id, provider)`.
|
|
87
|
+
|
|
88
|
+
Supported provider profiles:
|
|
89
|
+
|
|
90
|
+
- AWS
|
|
91
|
+
- NPM
|
|
92
|
+
- Jenkins Development
|
|
93
|
+
- Jenkins Production
|
|
94
|
+
- GHCR
|
|
95
|
+
|
|
96
|
+
Secrets use AES-256-GCM because provider APIs need the original credential later. Account passwords use bcrypt.
|
|
97
|
+
|
|
98
|
+
Users cannot resolve another user's provider credentials through the normal API. Provider resolution is audited and responses use `Cache-Control: no-store`.
|
|
99
|
+
|
|
100
|
+
## Managed AWS IAM credentials
|
|
101
|
+
|
|
102
|
+
When unified user creation generates an AWS access key, AWS returns the secret only at creation time. IndraQ can store that generated secret encrypted in `managed_aws_credentials` for the target Cloud user.
|
|
103
|
+
|
|
104
|
+
The target logged-in user can later resolve their own stored AWS credential. Users can rotate their own IndraQ-managed AWS key; managers/admins can rotate keys for users they are authorized to manage. Rotation creates the replacement key, stores it encrypted, updates the target provider credential, then deletes the previous managed key. Administrative creation/deletion/rotation and user resolution are audited.
|
|
105
|
+
|
|
106
|
+
## Password lifecycle
|
|
107
|
+
|
|
108
|
+
Users created through the management API receive `must_change_password=true`. The first successful login must be followed by `/auth/first-login-password` before normal protected operations are allowed. Managed NPM/Jenkins identities created through the unified workflow can be rotated to the new password as part of that first-login change.
|
|
109
|
+
|
|
110
|
+
Self-service password recovery uses `/auth/password-reset/request` and `/auth/password-reset/verify`. OTP values are bcrypt-hashed in PostgreSQL, expire after 10 minutes, are rate-limited, and have an attempt limit. SMTP must be configured for delivery. AWS is not a password-reset target because IndraQ AWS users are CLI/API access-key users.
|
|
111
|
+
|
|
112
|
+
## Shared environments
|
|
113
|
+
|
|
114
|
+
`environments` are admin-managed reusable server targets containing values such as:
|
|
115
|
+
|
|
116
|
+
- name
|
|
117
|
+
- description
|
|
118
|
+
- IPv4 address
|
|
119
|
+
- region
|
|
120
|
+
|
|
121
|
+
Environment names are arbitrary. Environments are reusable address targets only; Route53 health-check settings are chosen per DNS record. PRIMARY/SECONDARY roles are assigned by a project DNS routing configuration, not stored permanently on the environment.
|
|
122
|
+
|
|
123
|
+
## Projects and project resources
|
|
124
|
+
|
|
125
|
+
Projects are stored separately from reusable user credentials.
|
|
126
|
+
|
|
127
|
+
`project_resources` stores project-specific associations such as:
|
|
128
|
+
|
|
129
|
+
- GHCR image
|
|
130
|
+
- Jenkins job
|
|
131
|
+
- Route53 record/health-check metadata
|
|
132
|
+
- NPM proxy
|
|
133
|
+
- environment association
|
|
134
|
+
|
|
135
|
+
`project_members` controls project membership. Every authenticated organization user can discover/view the project catalog, while creators/members with WRITE (and managers/admins) may run infrastructure-changing workflows. In v2.0, adding a project member also inherits every project resource that already exists, creating matching `project_resource_grants` with VIEW or MANAGE permission. Removing the member removes that project's resource grants.
|
|
136
|
+
|
|
137
|
+
`project_resource_grants` records service/resource-level access. The CLI synchronizes native access where the provider supports safe granularity (for example exact Route53 record/health-check permissions and Jenkins item roles). Nginx Proxy Manager does not expose a safe per-proxy multi-owner ACL and GHCR direct local access requires the user's own GitHub/GHCR token, so the API never leaks an administrator credential to simulate those permissions.
|
|
138
|
+
|
|
139
|
+
## User deletion and project retention
|
|
140
|
+
|
|
141
|
+
User deletion is a hard delete. The CLI removes configured external identities first, then calls the Cloud delete endpoint last. The Cloud API reassigns shared resource ownership to the acting manager/admin before deleting the user row. Project definitions, environments, project resources, DNS/proxy/GHCR/Jenkins metadata and audit history are retained; user-scoped memberships, grants, integrations and managed credentials cascade with the deleted identity.
|
|
142
|
+
|
|
143
|
+
## Audit logs
|
|
144
|
+
|
|
145
|
+
Administrative and sensitive actions are written to `audit_logs`. Audit log access is restricted to managers/admins.
|
package/cloud-api/package.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@indraq/cloud-api",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.4.
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "tsx watch src/index.ts",
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"start": "node dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"bcryptjs": "^3.0.2",
|
|
13
|
-
"dotenv": "^17.2.2",
|
|
14
|
-
"express": "^5.1.0",
|
|
15
|
-
"jsonwebtoken": "^9.0.2",
|
|
16
|
-
"pg": "^8.16.3",
|
|
17
|
-
"nodemailer": "^6.9.16",
|
|
18
|
-
"@aws-sdk/client-iam": "^3.1123.0"
|
|
19
|
-
},
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@types/express": "^5.0.3",
|
|
22
|
-
"@types/jsonwebtoken": "^9.0.10",
|
|
23
|
-
"@types/node": "^24.3.0",
|
|
24
|
-
"@types/pg": "^8.15.5",
|
|
25
|
-
"tsx": "^4.20.5",
|
|
26
|
-
"typescript": "^5.9.2",
|
|
27
|
-
"@types/nodemailer": "^6.4.17"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@indraq/cloud-api",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.4.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"bcryptjs": "^3.0.2",
|
|
13
|
+
"dotenv": "^17.2.2",
|
|
14
|
+
"express": "^5.1.0",
|
|
15
|
+
"jsonwebtoken": "^9.0.2",
|
|
16
|
+
"pg": "^8.16.3",
|
|
17
|
+
"nodemailer": "^6.9.16",
|
|
18
|
+
"@aws-sdk/client-iam": "^3.1123.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/express": "^5.0.3",
|
|
22
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
23
|
+
"@types/node": "^24.3.0",
|
|
24
|
+
"@types/pg": "^8.15.5",
|
|
25
|
+
"tsx": "^4.20.5",
|
|
26
|
+
"typescript": "^5.9.2",
|
|
27
|
+
"@types/nodemailer": "^6.4.17"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
2
|
-
CREATE TABLE IF NOT EXISTS users (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), email text UNIQUE NOT NULL, name text NOT NULL, password_hash text NOT NULL, role text NOT NULL CHECK (role IN ('user','manager','admin')), is_active boolean NOT NULL DEFAULT true, created_by uuid REFERENCES users(id), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now());
|
|
3
|
-
CREATE TABLE IF NOT EXISTS projects (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, kind text NOT NULL, config jsonb NOT NULL DEFAULT '{}'::jsonb, created_by uuid NOT NULL REFERENCES users(id), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now());
|
|
4
|
-
CREATE TABLE IF NOT EXISTS project_members (project_id uuid REFERENCES projects(id) ON DELETE CASCADE, user_id uuid REFERENCES users(id) ON DELETE CASCADE, role text NOT NULL DEFAULT 'user', PRIMARY KEY(project_id,user_id));
|
|
5
|
-
CREATE TABLE IF NOT EXISTS servers (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL UNIQUE, environment text NOT NULL, ipv4 inet NOT NULL, health_path text DEFAULT '/health', is_backup boolean NOT NULL DEFAULT false, created_by uuid NOT NULL REFERENCES users(id), created_at timestamptz NOT NULL DEFAULT now());
|
|
6
|
-
CREATE TABLE IF NOT EXISTS project_integrations (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, provider text NOT NULL, config jsonb NOT NULL DEFAULT '{}'::jsonb, encrypted_secret text, UNIQUE(project_id,provider));
|
|
7
|
-
CREATE TABLE IF NOT EXISTS audit_logs (id bigserial PRIMARY KEY, actor_id uuid REFERENCES users(id), action text NOT NULL, resource_type text NOT NULL, resource_id text, reason text, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now());
|
|
8
|
-
CREATE INDEX IF NOT EXISTS audit_logs_created_at_idx ON audit_logs(created_at DESC);
|
|
1
|
+
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
2
|
+
CREATE TABLE IF NOT EXISTS users (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), email text UNIQUE NOT NULL, name text NOT NULL, password_hash text NOT NULL, role text NOT NULL CHECK (role IN ('user','manager','admin')), is_active boolean NOT NULL DEFAULT true, created_by uuid REFERENCES users(id), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now());
|
|
3
|
+
CREATE TABLE IF NOT EXISTS projects (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, kind text NOT NULL, config jsonb NOT NULL DEFAULT '{}'::jsonb, created_by uuid NOT NULL REFERENCES users(id), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now());
|
|
4
|
+
CREATE TABLE IF NOT EXISTS project_members (project_id uuid REFERENCES projects(id) ON DELETE CASCADE, user_id uuid REFERENCES users(id) ON DELETE CASCADE, role text NOT NULL DEFAULT 'user', PRIMARY KEY(project_id,user_id));
|
|
5
|
+
CREATE TABLE IF NOT EXISTS servers (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL UNIQUE, environment text NOT NULL, ipv4 inet NOT NULL, health_path text DEFAULT '/health', is_backup boolean NOT NULL DEFAULT false, created_by uuid NOT NULL REFERENCES users(id), created_at timestamptz NOT NULL DEFAULT now());
|
|
6
|
+
CREATE TABLE IF NOT EXISTS project_integrations (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, provider text NOT NULL, config jsonb NOT NULL DEFAULT '{}'::jsonb, encrypted_secret text, UNIQUE(project_id,provider));
|
|
7
|
+
CREATE TABLE IF NOT EXISTS audit_logs (id bigserial PRIMARY KEY, actor_id uuid REFERENCES users(id), action text NOT NULL, resource_type text NOT NULL, resource_id text, reason text, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now());
|
|
8
|
+
CREATE INDEX IF NOT EXISTS audit_logs_created_at_idx ON audit_logs(created_at DESC);
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
CREATE TABLE IF NOT EXISTS user_integrations (
|
|
2
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
3
|
-
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
4
|
-
provider text NOT NULL,
|
|
5
|
-
config jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
6
|
-
encrypted_secret text,
|
|
7
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
8
|
-
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
9
|
-
UNIQUE(user_id, provider)
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
CREATE INDEX IF NOT EXISTS user_integrations_user_idx
|
|
13
|
-
ON user_integrations(user_id, provider);
|
|
14
|
-
|
|
15
|
-
CREATE TABLE IF NOT EXISTS project_resources (
|
|
16
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
17
|
-
project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
18
|
-
environment text NOT NULL DEFAULT 'dev',
|
|
19
|
-
resource_type text NOT NULL,
|
|
20
|
-
provider text NOT NULL,
|
|
21
|
-
provider_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
22
|
-
external_id text,
|
|
23
|
-
config jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
24
|
-
created_by uuid REFERENCES users(id),
|
|
25
|
-
updated_by uuid REFERENCES users(id),
|
|
26
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
27
|
-
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
28
|
-
UNIQUE(project_id, environment, resource_type)
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
CREATE INDEX IF NOT EXISTS project_resources_project_idx
|
|
32
|
-
ON project_resources(project_id, environment, resource_type);
|
|
33
|
-
|
|
34
|
-
CREATE INDEX IF NOT EXISTS project_resources_provider_user_idx
|
|
35
|
-
ON project_resources(provider_user_id, provider);
|
|
36
|
-
|
|
37
|
-
-- provider credentials belong to an authenticated IndraQ user and are
|
|
38
|
-
-- reusable across every project that user can access. Project resources keep
|
|
39
|
-
-- only infrastructure associations plus the user whose provider profile last
|
|
40
|
-
-- reconciled the resource.
|
|
1
|
+
CREATE TABLE IF NOT EXISTS user_integrations (
|
|
2
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
3
|
+
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
4
|
+
provider text NOT NULL,
|
|
5
|
+
config jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
6
|
+
encrypted_secret text,
|
|
7
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
8
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
9
|
+
UNIQUE(user_id, provider)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
CREATE INDEX IF NOT EXISTS user_integrations_user_idx
|
|
13
|
+
ON user_integrations(user_id, provider);
|
|
14
|
+
|
|
15
|
+
CREATE TABLE IF NOT EXISTS project_resources (
|
|
16
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
17
|
+
project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
18
|
+
environment text NOT NULL DEFAULT 'dev',
|
|
19
|
+
resource_type text NOT NULL,
|
|
20
|
+
provider text NOT NULL,
|
|
21
|
+
provider_user_id uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
22
|
+
external_id text,
|
|
23
|
+
config jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
24
|
+
created_by uuid REFERENCES users(id),
|
|
25
|
+
updated_by uuid REFERENCES users(id),
|
|
26
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
27
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
28
|
+
UNIQUE(project_id, environment, resource_type)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
CREATE INDEX IF NOT EXISTS project_resources_project_idx
|
|
32
|
+
ON project_resources(project_id, environment, resource_type);
|
|
33
|
+
|
|
34
|
+
CREATE INDEX IF NOT EXISTS project_resources_provider_user_idx
|
|
35
|
+
ON project_resources(provider_user_id, provider);
|
|
36
|
+
|
|
37
|
+
-- provider credentials belong to an authenticated IndraQ user and are
|
|
38
|
+
-- reusable across every project that user can access. Project resources keep
|
|
39
|
+
-- only infrastructure associations plus the user whose provider profile last
|
|
40
|
+
-- reconciled the resource.
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
-- shared admin-managed named environments.
|
|
2
|
-
-- Environments are generic reusable targets. PRIMARY/SECONDARY is assigned only
|
|
3
|
-
-- when a project creates a FAILOVER Route53 record.
|
|
4
|
-
CREATE TABLE IF NOT EXISTS environments (
|
|
5
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
6
|
-
name text NOT NULL UNIQUE,
|
|
7
|
-
description text,
|
|
8
|
-
ipv4 inet NOT NULL,
|
|
9
|
-
region text,
|
|
10
|
-
health_path text NOT NULL DEFAULT '/health',
|
|
11
|
-
created_by uuid NOT NULL REFERENCES users(id),
|
|
12
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
13
|
-
updated_at timestamptz NOT NULL DEFAULT now()
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
CREATE INDEX IF NOT EXISTS environments_name_idx ON environments(name);
|
|
17
|
-
|
|
18
|
-
ALTER TABLE project_resources
|
|
19
|
-
ADD COLUMN IF NOT EXISTS environment_id uuid REFERENCES environments(id) ON DELETE SET NULL;
|
|
1
|
+
-- shared admin-managed named environments.
|
|
2
|
+
-- Environments are generic reusable targets. PRIMARY/SECONDARY is assigned only
|
|
3
|
+
-- when a project creates a FAILOVER Route53 record.
|
|
4
|
+
CREATE TABLE IF NOT EXISTS environments (
|
|
5
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
6
|
+
name text NOT NULL UNIQUE,
|
|
7
|
+
description text,
|
|
8
|
+
ipv4 inet NOT NULL,
|
|
9
|
+
region text,
|
|
10
|
+
health_path text NOT NULL DEFAULT '/health',
|
|
11
|
+
created_by uuid NOT NULL REFERENCES users(id),
|
|
12
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
13
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE INDEX IF NOT EXISTS environments_name_idx ON environments(name);
|
|
17
|
+
|
|
18
|
+
ALTER TABLE project_resources
|
|
19
|
+
ADD COLUMN IF NOT EXISTS environment_id uuid REFERENCES environments(id) ON DELETE SET NULL;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
-- managed AWS IAM credentials.
|
|
2
|
-
-- AWS only returns SecretAccessKey when an access key is created. IndraQ stores
|
|
3
|
-
-- the generated secret encrypted in Cloud so the owning IndraQ user can resolve
|
|
4
|
-
-- it later without relying on AWS to recover it.
|
|
5
|
-
CREATE TABLE IF NOT EXISTS managed_aws_credentials (
|
|
6
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
7
|
-
cloud_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
8
|
-
aws_username text NOT NULL,
|
|
9
|
-
access_key_id text NOT NULL,
|
|
10
|
-
encrypted_secret_access_key text NOT NULL,
|
|
11
|
-
indraq_role text NOT NULL CHECK (indraq_role IN ('user','manager','admin')),
|
|
12
|
-
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
13
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
14
|
-
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
15
|
-
UNIQUE(cloud_user_id, aws_username),
|
|
16
|
-
UNIQUE(access_key_id)
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
CREATE INDEX IF NOT EXISTS managed_aws_credentials_user_idx
|
|
20
|
-
ON managed_aws_credentials(cloud_user_id, aws_username);
|
|
1
|
+
-- managed AWS IAM credentials.
|
|
2
|
+
-- AWS only returns SecretAccessKey when an access key is created. IndraQ stores
|
|
3
|
+
-- the generated secret encrypted in Cloud so the owning IndraQ user can resolve
|
|
4
|
+
-- it later without relying on AWS to recover it.
|
|
5
|
+
CREATE TABLE IF NOT EXISTS managed_aws_credentials (
|
|
6
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
7
|
+
cloud_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
8
|
+
aws_username text NOT NULL,
|
|
9
|
+
access_key_id text NOT NULL,
|
|
10
|
+
encrypted_secret_access_key text NOT NULL,
|
|
11
|
+
indraq_role text NOT NULL CHECK (indraq_role IN ('user','manager','admin')),
|
|
12
|
+
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
13
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
14
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
15
|
+
UNIQUE(cloud_user_id, aws_username),
|
|
16
|
+
UNIQUE(access_key_id)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS managed_aws_credentials_user_idx
|
|
20
|
+
ON managed_aws_credentials(cloud_user_id, aws_username);
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
-- Project membership and per-resource access grants.
|
|
2
|
-
-- Project membership lets a user work with a project; resource grants control
|
|
3
|
-
-- which provider resources a normal user can read/update. Managers/admins can
|
|
4
|
-
-- manage membership and grants with audited reasons.
|
|
5
|
-
ALTER TABLE project_members
|
|
6
|
-
ADD COLUMN IF NOT EXISTS added_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
7
|
-
ADD COLUMN IF NOT EXISTS added_at timestamptz NOT NULL DEFAULT now();
|
|
8
|
-
|
|
9
|
-
CREATE TABLE IF NOT EXISTS project_resource_grants (
|
|
10
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
11
|
-
project_resource_id uuid NOT NULL REFERENCES project_resources(id) ON DELETE CASCADE,
|
|
12
|
-
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
13
|
-
permission text NOT NULL DEFAULT 'manage' CHECK (permission IN ('view','manage')),
|
|
14
|
-
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
15
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
16
|
-
UNIQUE(project_resource_id, user_id)
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
CREATE INDEX IF NOT EXISTS project_resource_grants_user_idx
|
|
20
|
-
ON project_resource_grants(user_id, project_resource_id);
|
|
21
|
-
CREATE INDEX IF NOT EXISTS project_resource_grants_resource_idx
|
|
22
|
-
ON project_resource_grants(project_resource_id, user_id);
|
|
1
|
+
-- Project membership and per-resource access grants.
|
|
2
|
+
-- Project membership lets a user work with a project; resource grants control
|
|
3
|
+
-- which provider resources a normal user can read/update. Managers/admins can
|
|
4
|
+
-- manage membership and grants with audited reasons.
|
|
5
|
+
ALTER TABLE project_members
|
|
6
|
+
ADD COLUMN IF NOT EXISTS added_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
7
|
+
ADD COLUMN IF NOT EXISTS added_at timestamptz NOT NULL DEFAULT now();
|
|
8
|
+
|
|
9
|
+
CREATE TABLE IF NOT EXISTS project_resource_grants (
|
|
10
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
11
|
+
project_resource_id uuid NOT NULL REFERENCES project_resources(id) ON DELETE CASCADE,
|
|
12
|
+
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
13
|
+
permission text NOT NULL DEFAULT 'manage' CHECK (permission IN ('view','manage')),
|
|
14
|
+
created_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
|
15
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
16
|
+
UNIQUE(project_resource_id, user_id)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS project_resource_grants_user_idx
|
|
20
|
+
ON project_resource_grants(user_id, project_resource_id);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS project_resource_grants_resource_idx
|
|
22
|
+
ON project_resource_grants(project_resource_id, user_id);
|