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.
Files changed (64) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1043 -1517
  3. package/cloud-api/.env +6 -0
  4. package/cloud-api/.env.example +14 -14
  5. package/cloud-api/README.md +145 -141
  6. package/cloud-api/package.json +29 -29
  7. package/cloud-api/sql/001_init.sql +8 -8
  8. package/cloud-api/sql/002_user_providers_and_project_resources.sql +40 -40
  9. package/cloud-api/sql/003_environment_infrastructure.sql +19 -19
  10. package/cloud-api/sql/004_managed_aws_credentials.sql +20 -20
  11. package/cloud-api/sql/005_project_members_and_resource_grants.sql +22 -22
  12. package/cloud-api/sql/006_catalog_identity_password_security.sql +96 -96
  13. package/cloud-api/sql/007_aws_access_key_rotation.sql +16 -16
  14. package/cloud-api/src/index.ts +1329 -1254
  15. package/cloud-api/tsconfig.json +18 -18
  16. package/dist/cli/help-content.d.ts.map +1 -1
  17. package/dist/cli/help-content.js +254 -251
  18. package/dist/cli/help-content.js.map +1 -1
  19. package/dist/modules/access/commands/access.command.d.ts +19 -0
  20. package/dist/modules/access/commands/access.command.d.ts.map +1 -1
  21. package/dist/modules/access/commands/access.command.js +57 -46
  22. package/dist/modules/access/commands/access.command.js.map +1 -1
  23. package/dist/modules/cloud/commands/cloud.command.d.ts.map +1 -1
  24. package/dist/modules/cloud/commands/cloud.command.js +18 -2
  25. package/dist/modules/cloud/commands/cloud.command.js.map +1 -1
  26. package/dist/modules/cloud/config/cloud-config.d.ts +1 -1
  27. package/dist/modules/cloud/config/cloud-config.js +1 -1
  28. package/dist/modules/cloud/services/cloud-jenkins.service.d.ts +13 -1
  29. package/dist/modules/cloud/services/cloud-jenkins.service.d.ts.map +1 -1
  30. package/dist/modules/cloud/services/cloud-jenkins.service.js +173 -77
  31. package/dist/modules/cloud/services/cloud-jenkins.service.js.map +1 -1
  32. package/dist/modules/cloud/services/managed-aws-credential.service.d.ts.map +1 -1
  33. package/dist/modules/cloud/services/managed-aws-credential.service.js +11 -4
  34. package/dist/modules/cloud/services/managed-aws-credential.service.js.map +1 -1
  35. package/dist/modules/jenkins/commands/admin.command.d.ts +3 -0
  36. package/dist/modules/jenkins/commands/admin.command.d.ts.map +1 -1
  37. package/dist/modules/jenkins/commands/admin.command.js +91 -1
  38. package/dist/modules/jenkins/commands/admin.command.js.map +1 -1
  39. package/dist/modules/jenkins/index.d.ts.map +1 -1
  40. package/dist/modules/jenkins/index.js +3 -0
  41. package/dist/modules/jenkins/index.js.map +1 -1
  42. package/dist/modules/scaffold/services/docker-template.service.js +95 -95
  43. package/dist/modules/users/commands/user.command.d.ts.map +1 -1
  44. package/dist/modules/users/commands/user.command.js +290 -44
  45. package/dist/modules/users/commands/user.command.js.map +1 -1
  46. package/dist/modules/users/providers/aws-user.provider.d.ts.map +1 -1
  47. package/dist/modules/users/providers/aws-user.provider.js +9 -2
  48. package/dist/modules/users/providers/aws-user.provider.js.map +1 -1
  49. package/dist/modules/users/providers/jenkins-user.provider.d.ts +11 -4
  50. package/dist/modules/users/providers/jenkins-user.provider.d.ts.map +1 -1
  51. package/dist/modules/users/providers/jenkins-user.provider.js +174 -72
  52. package/dist/modules/users/providers/jenkins-user.provider.js.map +1 -1
  53. package/dist/modules/users/providers/npm-user.provider.d.ts.map +1 -1
  54. package/dist/modules/users/providers/npm-user.provider.js +3 -1
  55. package/dist/modules/users/providers/npm-user.provider.js.map +1 -1
  56. package/dist/shared/config/config-files.js +7 -7
  57. package/docs/ARCHITECTURE.md +29 -29
  58. package/docs/CREATE-DEPLOYMENT-reference.groovy +1105 -1105
  59. package/package.json +77 -76
  60. package/templates/jenkins/CREATE-DEPLOYMENT.groovy +1105 -1105
  61. package/templates/jenkins/Jenkinsfile-Mobile-App +759 -759
  62. package/cloud-api/.dockerignore +0 -18
  63. package/cloud-api/Dockerfile +0 -19
  64. 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
@@ -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>
@@ -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 WRITE membership. Every authenticated organization user can discover/view the project catalog, while creators/members with WRITE (and managers/admins) may run infrastructure-changing workflows. Managers/admins can add or remove members with an audit reason.
136
-
137
- `project_resource_grants` records service/resource-level access granted by managers/admins. IndraQ synchronizes native access where the provider supports safe granularity (for example exact Route53 record/health-check permissions and Jenkins item roles) while retaining IndraQ Cloud as the authoritative access inventory.
138
-
139
- ## Audit logs
140
-
141
- Administrative and sensitive actions are written to `audit_logs`. Audit log access is restricted to managers/admins.
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.
@@ -1,29 +1,29 @@
1
- {
2
- "name": "@indraq/cloud-api",
3
- "private": true,
4
- "version": "0.4.0",
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);