meyi-marketplace-onboarding-dev 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +62 -0
  2. package/onboarding-service/README.md +115 -0
  3. package/onboarding-service/dist/applications/meyi-connect/aws-config.d.ts +3 -0
  4. package/onboarding-service/dist/applications/meyi-connect/aws-config.d.ts.map +1 -0
  5. package/onboarding-service/dist/applications/meyi-connect/aws-config.js +17 -0
  6. package/onboarding-service/dist/applications/meyi-connect/aws-config.js.map +1 -0
  7. package/onboarding-service/dist/applications/meyi-connect/config.d.ts +3 -0
  8. package/onboarding-service/dist/applications/meyi-connect/config.d.ts.map +1 -0
  9. package/onboarding-service/dist/applications/meyi-connect/config.js +38 -0
  10. package/onboarding-service/dist/applications/meyi-connect/config.js.map +1 -0
  11. package/onboarding-service/dist/applications/meyi-connect/plugin-config.d.ts +6 -0
  12. package/onboarding-service/dist/applications/meyi-connect/plugin-config.d.ts.map +1 -0
  13. package/onboarding-service/dist/applications/meyi-connect/plugin-config.js +70 -0
  14. package/onboarding-service/dist/applications/meyi-connect/plugin-config.js.map +1 -0
  15. package/onboarding-service/dist/applications/meyi-connect/provisioner.d.ts +4 -0
  16. package/onboarding-service/dist/applications/meyi-connect/provisioner.d.ts.map +1 -0
  17. package/onboarding-service/dist/applications/meyi-connect/provisioner.js +120 -0
  18. package/onboarding-service/dist/applications/meyi-connect/provisioner.js.map +1 -0
  19. package/onboarding-service/dist/applications/registry.d.ts +9 -0
  20. package/onboarding-service/dist/applications/registry.d.ts.map +1 -0
  21. package/onboarding-service/dist/applications/registry.js +38 -0
  22. package/onboarding-service/dist/applications/registry.js.map +1 -0
  23. package/onboarding-service/dist/applications/types.d.ts +57 -0
  24. package/onboarding-service/dist/applications/types.d.ts.map +1 -0
  25. package/onboarding-service/dist/applications/types.js +3 -0
  26. package/onboarding-service/dist/applications/types.js.map +1 -0
  27. package/onboarding-service/dist/db/index.d.ts +13 -0
  28. package/onboarding-service/dist/db/index.d.ts.map +1 -0
  29. package/onboarding-service/dist/db/index.js +76 -0
  30. package/onboarding-service/dist/db/index.js.map +1 -0
  31. package/onboarding-service/dist/db/insight-schema.d.ts +222 -0
  32. package/onboarding-service/dist/db/insight-schema.d.ts.map +1 -0
  33. package/onboarding-service/dist/db/insight-schema.js +40 -0
  34. package/onboarding-service/dist/db/insight-schema.js.map +1 -0
  35. package/onboarding-service/dist/db/migrate.d.ts +13 -0
  36. package/onboarding-service/dist/db/migrate.d.ts.map +1 -0
  37. package/onboarding-service/dist/db/migrate.js +103 -0
  38. package/onboarding-service/dist/db/migrate.js.map +1 -0
  39. package/onboarding-service/dist/db/migration-cli.d.ts +2 -0
  40. package/onboarding-service/dist/db/migration-cli.d.ts.map +1 -0
  41. package/onboarding-service/dist/db/migration-cli.js +8 -0
  42. package/onboarding-service/dist/db/migration-cli.js.map +1 -0
  43. package/onboarding-service/dist/db/schema.d.ts +1674 -0
  44. package/onboarding-service/dist/db/schema.d.ts.map +1 -0
  45. package/onboarding-service/dist/db/schema.js +267 -0
  46. package/onboarding-service/dist/db/schema.js.map +1 -0
  47. package/onboarding-service/dist/index.d.ts +12 -0
  48. package/onboarding-service/dist/index.d.ts.map +1 -0
  49. package/onboarding-service/dist/index.js +127 -0
  50. package/onboarding-service/dist/index.js.map +1 -0
  51. package/onboarding-service/dist/modules/marketplace/admin-service.d.ts +96 -0
  52. package/onboarding-service/dist/modules/marketplace/admin-service.d.ts.map +1 -0
  53. package/onboarding-service/dist/modules/marketplace/admin-service.js +247 -0
  54. package/onboarding-service/dist/modules/marketplace/admin-service.js.map +1 -0
  55. package/onboarding-service/dist/modules/marketplace/agreements.d.ts +22 -0
  56. package/onboarding-service/dist/modules/marketplace/agreements.d.ts.map +1 -0
  57. package/onboarding-service/dist/modules/marketplace/agreements.js +99 -0
  58. package/onboarding-service/dist/modules/marketplace/agreements.js.map +1 -0
  59. package/onboarding-service/dist/modules/marketplace/aws-controller.d.ts +25 -0
  60. package/onboarding-service/dist/modules/marketplace/aws-controller.d.ts.map +1 -0
  61. package/onboarding-service/dist/modules/marketplace/aws-controller.js +337 -0
  62. package/onboarding-service/dist/modules/marketplace/aws-controller.js.map +1 -0
  63. package/onboarding-service/dist/modules/marketplace/aws-service.d.ts +235 -0
  64. package/onboarding-service/dist/modules/marketplace/aws-service.d.ts.map +1 -0
  65. package/onboarding-service/dist/modules/marketplace/aws-service.js +1651 -0
  66. package/onboarding-service/dist/modules/marketplace/aws-service.js.map +1 -0
  67. package/onboarding-service/dist/modules/marketplace/controller.d.ts +78 -0
  68. package/onboarding-service/dist/modules/marketplace/controller.d.ts.map +1 -0
  69. package/onboarding-service/dist/modules/marketplace/controller.js +601 -0
  70. package/onboarding-service/dist/modules/marketplace/controller.js.map +1 -0
  71. package/onboarding-service/dist/modules/marketplace/cost-service.d.ts +38 -0
  72. package/onboarding-service/dist/modules/marketplace/cost-service.d.ts.map +1 -0
  73. package/onboarding-service/dist/modules/marketplace/cost-service.js +204 -0
  74. package/onboarding-service/dist/modules/marketplace/cost-service.js.map +1 -0
  75. package/onboarding-service/dist/modules/marketplace/email-service.d.ts +15 -0
  76. package/onboarding-service/dist/modules/marketplace/email-service.d.ts.map +1 -0
  77. package/onboarding-service/dist/modules/marketplace/email-service.js +81 -0
  78. package/onboarding-service/dist/modules/marketplace/email-service.js.map +1 -0
  79. package/onboarding-service/dist/modules/marketplace/entitlement.d.ts +26 -0
  80. package/onboarding-service/dist/modules/marketplace/entitlement.d.ts.map +1 -0
  81. package/onboarding-service/dist/modules/marketplace/entitlement.js +128 -0
  82. package/onboarding-service/dist/modules/marketplace/entitlement.js.map +1 -0
  83. package/onboarding-service/dist/modules/marketplace/middleware.d.ts +45 -0
  84. package/onboarding-service/dist/modules/marketplace/middleware.d.ts.map +1 -0
  85. package/onboarding-service/dist/modules/marketplace/middleware.js +169 -0
  86. package/onboarding-service/dist/modules/marketplace/middleware.js.map +1 -0
  87. package/onboarding-service/dist/modules/marketplace/notifications.d.ts +48 -0
  88. package/onboarding-service/dist/modules/marketplace/notifications.d.ts.map +1 -0
  89. package/onboarding-service/dist/modules/marketplace/notifications.js +214 -0
  90. package/onboarding-service/dist/modules/marketplace/notifications.js.map +1 -0
  91. package/onboarding-service/dist/modules/marketplace/resolve-customer.d.ts +24 -0
  92. package/onboarding-service/dist/modules/marketplace/resolve-customer.d.ts.map +1 -0
  93. package/onboarding-service/dist/modules/marketplace/resolve-customer.js +100 -0
  94. package/onboarding-service/dist/modules/marketplace/resolve-customer.js.map +1 -0
  95. package/onboarding-service/dist/modules/marketplace/routes.d.ts +12 -0
  96. package/onboarding-service/dist/modules/marketplace/routes.d.ts.map +1 -0
  97. package/onboarding-service/dist/modules/marketplace/routes.js +128 -0
  98. package/onboarding-service/dist/modules/marketplace/routes.js.map +1 -0
  99. package/onboarding-service/dist/modules/marketplace/seller-credentials.d.ts +28 -0
  100. package/onboarding-service/dist/modules/marketplace/seller-credentials.d.ts.map +1 -0
  101. package/onboarding-service/dist/modules/marketplace/seller-credentials.js +74 -0
  102. package/onboarding-service/dist/modules/marketplace/seller-credentials.js.map +1 -0
  103. package/onboarding-service/dist/modules/marketplace/service.d.ts +85 -0
  104. package/onboarding-service/dist/modules/marketplace/service.d.ts.map +1 -0
  105. package/onboarding-service/dist/modules/marketplace/service.js +719 -0
  106. package/onboarding-service/dist/modules/marketplace/service.js.map +1 -0
  107. package/onboarding-service/dist/modules/marketplace/types.d.ts +81 -0
  108. package/onboarding-service/dist/modules/marketplace/types.d.ts.map +1 -0
  109. package/onboarding-service/dist/modules/marketplace/types.js +14 -0
  110. package/onboarding-service/dist/modules/marketplace/types.js.map +1 -0
  111. package/onboarding-service/dist/modules/marketplace/utils.d.ts +80 -0
  112. package/onboarding-service/dist/modules/marketplace/utils.d.ts.map +1 -0
  113. package/onboarding-service/dist/modules/marketplace/utils.js +168 -0
  114. package/onboarding-service/dist/modules/marketplace/utils.js.map +1 -0
  115. package/onboarding-service/dist/paths.d.ts +4 -0
  116. package/onboarding-service/dist/paths.d.ts.map +1 -0
  117. package/onboarding-service/dist/paths.js +19 -0
  118. package/onboarding-service/dist/paths.js.map +1 -0
  119. package/onboarding-service/dist/standalone.d.ts +2 -0
  120. package/onboarding-service/dist/standalone.d.ts.map +1 -0
  121. package/onboarding-service/dist/standalone.js +26 -0
  122. package/onboarding-service/dist/standalone.js.map +1 -0
  123. package/onboarding-service/drizzle/0001_marketplace_tables.sql +76 -0
  124. package/onboarding-service/drizzle/0002_loose_paper_doll.sql +121 -0
  125. package/onboarding-service/drizzle/0002_onboarding_details.sql +18 -0
  126. package/onboarding-service/drizzle/0003_tiresome_millenium_guard.sql +4 -0
  127. package/onboarding-service/drizzle/0004_onboarding_plugins.sql +5 -0
  128. package/onboarding-service/drizzle/0005_shared_tenant_tables.sql +38 -0
  129. package/onboarding-service/drizzle/0006_aws_connection_capabilities.sql +11 -0
  130. package/onboarding-service/drizzle/0007_stackset_operation_tracking.sql +13 -0
  131. package/onboarding-service/drizzle/0008_stack_instances.sql +29 -0
  132. package/onboarding-service/drizzle/0009_region_mode.sql +6 -0
  133. package/onboarding-service/drizzle/0010_account_scope.sql +5 -0
  134. package/onboarding-service/drizzle/0011_tenant_uuid.sql +186 -0
  135. package/onboarding-service/drizzle/0012_user_email_identity.sql +28 -0
  136. package/onboarding-service/drizzle/0013_tenant_owner_uniqueness.sql +57 -0
  137. package/onboarding-service/drizzle/0014_cost_cur_entitlement.sql +31 -0
  138. package/onboarding-service/drizzle/0015_tenant_aws_account_uniqueness.sql +58 -0
  139. package/onboarding-service/drizzle/0016_marketplace_applications.sql +31 -0
  140. package/onboarding-service/drizzle/meta/0002_snapshot.json +924 -0
  141. package/onboarding-service/drizzle/meta/0003_snapshot.json +949 -0
  142. package/onboarding-service/drizzle/meta/_journal.json +125 -0
  143. package/onboarding-ui/access-verification.html +886 -0
  144. package/onboarding-ui/admin.html +263 -0
  145. package/onboarding-ui/assets/css/01-base.css +3 -0
  146. package/onboarding-ui/assets/css/02-sidebar.css +3 -0
  147. package/onboarding-ui/assets/css/03-responsive.css +32 -0
  148. package/onboarding-ui/assets/css/04-plugins.css +10 -0
  149. package/onboarding-ui/assets/css/05-accounts.css +2 -0
  150. package/onboarding-ui/assets/css/06-organization.css +6 -0
  151. package/onboarding-ui/assets/css/07-complete.css +2 -0
  152. package/onboarding-ui/assets/js/api.js +7 -0
  153. package/onboarding-ui/assets/js/capabilities.js +27 -0
  154. package/onboarding-ui/assets/js/config.js +37 -0
  155. package/onboarding-ui/assets/js/deployment.js +19 -0
  156. package/onboarding-ui/assets/js/dom.js +7 -0
  157. package/onboarding-ui/assets/js/launch.js +31 -0
  158. package/onboarding-ui/assets/js/main.js +46 -0
  159. package/onboarding-ui/assets/js/onboarding.js +81 -0
  160. package/onboarding-ui/assets/js/otp.js +7 -0
  161. package/onboarding-ui/assets/js/password.js +16 -0
  162. package/onboarding-ui/assets/js/plugins.js +45 -0
  163. package/onboarding-ui/assets/js/stackset-rows.js +13 -0
  164. package/onboarding-ui/assets/js/state.js +5 -0
  165. package/onboarding-ui/assets/js/templates.js +8 -0
  166. package/onboarding-ui/assets/js/views.js +11 -0
  167. package/onboarding-ui/login.html +467 -0
  168. package/onboarding-ui/onboarding.html +30 -0
  169. package/onboarding-ui/pricing.html +383 -0
  170. package/onboarding-ui/register.html +553 -0
  171. package/onboarding-ui/subscription.html +681 -0
  172. package/onboarding-ui/subscription_checkout.html +547 -0
  173. package/onboarding-ui/templates/aws-onboarding-stackset.yaml +191 -0
  174. package/onboarding-ui/templates/aws-onboarding.yaml +58 -0
  175. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Marketplace Onboarding
2
+
3
+ Reusable AWS Marketplace customer-onboarding npm module. The API, migrations,
4
+ application adapters, static UI, and CloudFormation templates ship as one package.
5
+
6
+ ```text
7
+ marketplace-onboarding/
8
+ |-- onboarding-service/ Express API, application adapters, AWS orchestration, and migrations
9
+ |-- onboarding-ui/ Static onboarding pages, assets, and CloudFormation templates
10
+ |-- package.json meyi-marketplace-onboarding package definition
11
+ `-- README.md
12
+ ```
13
+
14
+ The package is import-safe: importing it does not start a server, run migrations,
15
+ or schedule jobs. A host application opts into those operations through the
16
+ exported API. The same source remains deployable as the standalone
17
+ `meyiconnect/customer-onboarding` Docker image.
18
+
19
+ ## Build And Verify
20
+
21
+ ```bash
22
+ cd marketplace-onboarding
23
+ npm run typecheck
24
+ npm run check:applications
25
+ ```
26
+
27
+ The UI has no build step. Validate its JavaScript modules from the repository root:
28
+
29
+ ```bash
30
+ node --check marketplace-onboarding/onboarding-ui/assets/js/main.js
31
+ ```
32
+
33
+ Build the combined image from this directory:
34
+
35
+ ```bash
36
+ cd marketplace-onboarding
37
+ docker build -t meyiconnect/customer-onboarding:dev .
38
+ ```
39
+
40
+ Inside the image, the UI is served under `/marketplace/ui` and
41
+ `/customer-onboarding/ui`.
42
+
43
+ ## Use In Meyi Connect
44
+
45
+ Build the package and install its direct sibling dependency in Meyi Connect:
46
+
47
+ ```bash
48
+ cd marketplace-onboarding
49
+ npm run build
50
+ cd ../../meyi-connect/backend
51
+ npm install
52
+ ```
53
+
54
+ Meyi Connect mounts `marketplaceNotificationParser()` before its global body
55
+ parser, runs `runMarketplaceMigrations()`, mounts `createMarketplaceRouter()`,
56
+ and starts `startMarketplaceJobs()` after the HTTP server starts. The bundled UI
57
+ is served by the router, so the Meyi Connect frontend does not need a second npm
58
+ dependency.
59
+
60
+ Marketplace onboarding enables automatically when the product code and both SNS
61
+ topic ARNs are present. `DATABASE_URL`, `JWT_SECRET`, `AWS_REGION`,
62
+ `FRONTEND_URL`, and `MARKETPLACE_ADMIN_SECRET` are also required once enabled.
@@ -0,0 +1,115 @@
1
+ # Marketplace Onboarding
2
+
3
+ Reusable AWS Marketplace registration and customer-onboarding service. The repository currently registers the **Meyi Connect** application, while the service and UI select application-specific behavior through a `MarketplaceApplication` adapter.
4
+
5
+ ## Reuse Decision
6
+
7
+ `onboarding-ui` is the customer-onboarding UI and `onboarding-service` is its API. Meyi Connect should use this existing flow.
8
+
9
+ - Do not create separate `setup-ui` or `setup-api` projects.
10
+ - Keep shared Marketplace registration, verification, password, plugin selection, and AWS orchestration in this repository.
11
+ - Put application-specific product identity, plugin rules, AWS template settings, tenant provisioning, and destination URLs in an application adapter.
12
+ - Existing records are assigned to `meyi-connect` by migration `0016_marketplace_applications.sql`, preserving the current flow.
13
+
14
+ ## Architecture
15
+
16
+ ```text
17
+ AWS Marketplace registration / SNS notifications
18
+ |
19
+ v
20
+ onboarding-service routes
21
+ |
22
+ v
23
+ MarketplaceApplication registry
24
+ |
25
+ +---------+---------+
26
+ | |
27
+ Meyi Connect adapter future app adapter
28
+ |
29
+ +-- product and plugin catalog
30
+ +-- AWS role, StackSet, and template settings
31
+ +-- tenant provisioning and plugin persistence
32
+ +-- sign-in and dashboard URLs
33
+
34
+ onboarding-ui -- GET /marketplace/application-config --> selected adapter
35
+ ```
36
+
37
+ The shared database rows carry `application_id` on marketplace customers, events, onboarding sessions, and AWS connections. Customer identity is unique per `(application_id, aws_customer_identifier)` so another product cannot collide with Meyi Connect.
38
+
39
+ ## Current Application
40
+
41
+ | Setting | Value |
42
+ |---|---|
43
+ | Application ID | `meyi-connect` |
44
+ | Product ID | `prod-2wjamdfmjqg66` |
45
+ | Adapter | `src/applications/meyi-connect/config.ts` |
46
+ | Plugin catalog | `src/applications/meyi-connect/plugin-config.ts` |
47
+ | AWS settings | `src/applications/meyi-connect/aws-config.ts` |
48
+ | Provisioning | `src/applications/meyi-connect/provisioner.ts` |
49
+
50
+ Meyi Connect retains its current plugin behavior: Pulse is required and visible, Insight is selected by default, Cost is opt-in, and Access is a required backend capability hidden from the selection screen.
51
+
52
+ ## Add Another Application
53
+
54
+ 1. Create `src/applications/<application-id>/` with configuration, plugin normalization, and a provisioner implementing `MarketplaceApplication` from `src/applications/types.ts`.
55
+ 2. Register the adapter in `src/applications/registry.ts`.
56
+ 3. Add product-specific environment variables and SNS topic validation when the new AWS Marketplace product is configured.
57
+ 4. Add any product-specific CloudFormation template under `iam/` and point the adapter's `templatePath` to it.
58
+ 5. Test registration, onboarding resume, plugin persistence, AWS connection creation, provisioning, and dashboard redirect for both applications.
59
+
60
+ Adding an adapter does not require cloning the UI or API. Open the UI with `?application=<application-id>` when no Marketplace session determines the application.
61
+
62
+ ## API
63
+
64
+ | Method | Path | Description |
65
+ |---|---|---|
66
+ | GET | `/marketplace/health` | Runtime health and AWS deployment settings |
67
+ | GET | `/marketplace/application-config` | Public configuration for the default application |
68
+ | GET | `/marketplace/application-config?applicationId=<id>` | Public configuration for a registered application |
69
+ | GET/POST | `/marketplace/register` | AWS Marketplace redirect handler |
70
+ | POST | `/marketplace/notifications` | SNS webhook with signature validation |
71
+ | GET | `/marketplace/subscription` | Authenticated subscription details |
72
+ | POST | `/marketplace/onboarding` | Create an application-scoped onboarding session |
73
+ | POST | `/marketplace/api/aws/connect` | Create an application-scoped AWS connection |
74
+ | POST | `/marketplace/sync` | Admin subscription synchronization |
75
+
76
+ ## Configuration
77
+
78
+ ```env
79
+ MARKETPLACE_DEFAULT_APPLICATION_ID=meyi-connect
80
+ MARKETPLACE_APP_NAME=Meyi Connect
81
+ AWS_MARKETPLACE_PRODUCT_CODE=<seller-product-code>
82
+ AWS_MARKETPLACE_PRODUCT_ID=prod-2wjamdfmjqg66
83
+ AWS_MARKETPLACE_SUBSCRIPTION_TOPIC_ARN=<subscription-topic-arn>
84
+ AWS_MARKETPLACE_ENTITLEMENT_TOPIC_ARN=<entitlement-topic-arn>
85
+ APP_URL=http://localhost:5173
86
+ DATABASE_URL=<postgres-connection-string>
87
+ JWT_SECRET=<shared-jwt-secret>
88
+ ```
89
+
90
+ See the repository `.env.example` for the full list. Never commit real credentials or admin secrets.
91
+
92
+ ## Build And Run
93
+
94
+ ```bash
95
+ cd marketplace-onboarding/onboarding-service
96
+ npm install
97
+ npm run typecheck
98
+ npm run check:applications
99
+ npm run db:migrate
100
+ npm start
101
+ ```
102
+
103
+ The service listens on `PORT` or `MARKETPLACE_SERVICE_PORT` (default `3020`). The Docker image copies `onboarding-ui` into the service and exposes the onboarding page through the configured `/marketplace` or `/customer-onboarding` base path.
104
+
105
+ ## Migration
106
+
107
+ Run `npm run db:migrate` before deploying this version. Migration `0016_marketplace_applications.sql` backfills all existing rows to `meyi-connect`, makes `application_id` required, and replaces the old globally unique customer identifier index with an application-scoped unique index.
108
+
109
+ ## Security
110
+
111
+ - Marketplace product identity is resolved server-side from AWS product codes.
112
+ - Client-supplied application IDs are checked against the registry and against persisted onboarding sessions.
113
+ - SNS signatures are validated before notification processing.
114
+ - Plans are sourced from AWS entitlements, never from client input.
115
+ - JWT verification allows only `HS256` and uses the shared application secret.
@@ -0,0 +1,3 @@
1
+ import type { ApplicationAwsConfig } from '../types';
2
+ export declare const meyiConnectAwsConfig: ApplicationAwsConfig;
3
+ //# sourceMappingURL=aws-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-config.d.ts","sourceRoot":"","sources":["../../../src/applications/meyi-connect/aws-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAIrD,eAAO,MAAM,oBAAoB,EAAE,oBAOlC,CAAC"}
@@ -0,0 +1,17 @@
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.meyiConnectAwsConfig = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const paths_1 = require("../../paths");
9
+ exports.meyiConnectAwsConfig = {
10
+ defaultRoleName: 'MeyiConnectOnboardingRole',
11
+ stackSetPrefix: 'MeyiConnect',
12
+ downloadFilePrefix: 'meyi-connect-stackset',
13
+ managedByTag: 'MeyiConnect',
14
+ templatePath: path_1.default.join((0, paths_1.getMarketplaceUiDirectory)(), 'templates', 'aws-onboarding-stackset.yaml'),
15
+ sessionNamePrefix: 'MeyiConnect',
16
+ };
17
+ //# sourceMappingURL=aws-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-config.js","sourceRoot":"","sources":["../../../src/applications/meyi-connect/aws-config.ts"],"names":[],"mappings":";;;;;;AACA,gDAAwB;AACxB,uCAAwD;AAE3C,QAAA,oBAAoB,GAAyB;IACxD,eAAe,EAAE,2BAA2B;IAC5C,cAAc,EAAE,aAAa;IAC7B,kBAAkB,EAAE,uBAAuB;IAC3C,YAAY,EAAE,aAAa;IAC3B,YAAY,EAAE,cAAI,CAAC,IAAI,CAAC,IAAA,iCAAyB,GAAE,EAAE,WAAW,EAAE,8BAA8B,CAAC;IACjG,iBAAiB,EAAE,aAAa;CACjC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { MarketplaceApplication } from '../types';
2
+ export declare function createMeyiConnectApplication(): MarketplaceApplication;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/applications/meyi-connect/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AASvD,wBAAgB,4BAA4B,IAAI,sBAAsB,CA8BrE"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMeyiConnectApplication = createMeyiConnectApplication;
4
+ const utils_1 = require("../../modules/marketplace/utils");
5
+ const aws_config_1 = require("./aws-config");
6
+ const plugin_config_1 = require("./plugin-config");
7
+ const provisioner_1 = require("./provisioner");
8
+ function createMeyiConnectApplication() {
9
+ const application = {
10
+ id: 'meyi-connect',
11
+ name: process.env.MARKETPLACE_APP_NAME || 'Meyi Connect',
12
+ productCode: process.env.AWS_MARKETPLACE_PRODUCT_CODE || '',
13
+ productId: process.env.AWS_MARKETPLACE_PRODUCT_ID || '',
14
+ databaseSchema: 'meyiconnect',
15
+ aws: aws_config_1.meyiConnectAwsConfig,
16
+ plugins: plugin_config_1.meyiConnectPlugins,
17
+ normalizeOnboardingPlugins: plugin_config_1.normalizeMeyiConnectOnboardingPlugins,
18
+ normalizePlugins: plugin_config_1.normalizeMeyiConnectPlugins,
19
+ provisionTenant: provisioner_1.provisionMeyiConnectTenant,
20
+ syncTenantPlugins: provisioner_1.syncMeyiConnectTenantPlugins,
21
+ signInUrl: utils_1.appSignInUrl,
22
+ onboardingCompleteUrl: (workspaceName) => `${process.env.FRONTEND_URL || ''}/ui/login.html?provisioned=1&workspace=${encodeURIComponent(workspaceName)}`,
23
+ dashboardUrl: utils_1.appBaseUrl,
24
+ toPublicConfig: () => ({
25
+ id: application.id,
26
+ name: application.name,
27
+ productId: application.productId,
28
+ loginUrl: application.signInUrl(),
29
+ dashboardUrl: application.dashboardUrl(),
30
+ awsRoleName: application.aws.defaultRoleName,
31
+ downloadFilePrefix: application.aws.downloadFilePrefix,
32
+ features: { awsOrganization: true },
33
+ plugins: application.plugins,
34
+ }),
35
+ };
36
+ return application;
37
+ }
38
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/applications/meyi-connect/config.ts"],"names":[],"mappings":";;AAUA,oEA8BC;AAxCD,2DAA2E;AAE3E,6CAAoD;AACpD,mDAIyB;AACzB,+CAAyF;AAEzF,SAAgB,4BAA4B;IAC1C,MAAM,WAAW,GAA2B;QAC1C,EAAE,EAAE,cAAc;QAClB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,cAAc;QACxD,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE;QAC3D,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,EAAE;QACvD,cAAc,EAAE,aAAa;QAC7B,GAAG,EAAE,iCAAoB;QACzB,OAAO,EAAE,kCAAkB;QAC3B,0BAA0B,EAAE,qDAAqC;QACjE,gBAAgB,EAAE,2CAA2B;QAC7C,eAAe,EAAE,wCAA0B;QAC3C,iBAAiB,EAAE,0CAA4B;QAC/C,SAAS,EAAE,oBAAY;QACvB,qBAAqB,EAAE,CAAC,aAAa,EAAE,EAAE,CACvC,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,0CAA0C,kBAAkB,CAAC,aAAa,CAAC,EAAE;QAChH,YAAY,EAAE,kBAAU;QACxB,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;YACrB,EAAE,EAAE,WAAW,CAAC,EAAE;YAClB,IAAI,EAAE,WAAW,CAAC,IAAI;YACtB,SAAS,EAAE,WAAW,CAAC,SAAS;YAChC,QAAQ,EAAE,WAAW,CAAC,SAAS,EAAE;YACjC,YAAY,EAAE,WAAW,CAAC,YAAY,EAAE;YACxC,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,eAAe;YAC5C,kBAAkB,EAAE,WAAW,CAAC,GAAG,CAAC,kBAAkB;YACtD,QAAQ,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;YACnC,OAAO,EAAE,WAAW,CAAC,OAAO;SAC7B,CAAC;KACH,CAAC;IACF,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ApplicationPlugin } from '../types';
2
+ export declare const meyiConnectPlugins: ApplicationPlugin[];
3
+ export declare function normalizeMeyiConnectOnboardingPlugins(plugins: unknown): string[];
4
+ export declare function normalizeMeyiConnectPersistedPlugins(plugins: unknown): string[];
5
+ export declare function normalizeMeyiConnectPlugins(plugins: unknown): string[];
6
+ //# sourceMappingURL=plugin-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-config.d.ts","sourceRoot":"","sources":["../../../src/applications/meyi-connect/plugin-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,eAAO,MAAM,kBAAkB,EAAE,iBAAiB,EAiCjD,CAAC;AAEF,wBAAgB,qCAAqC,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAMhF;AAED,wBAAgB,oCAAoC,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAY/E;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAUtE"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.meyiConnectPlugins = void 0;
4
+ exports.normalizeMeyiConnectOnboardingPlugins = normalizeMeyiConnectOnboardingPlugins;
5
+ exports.normalizeMeyiConnectPersistedPlugins = normalizeMeyiConnectPersistedPlugins;
6
+ exports.normalizeMeyiConnectPlugins = normalizeMeyiConnectPlugins;
7
+ exports.meyiConnectPlugins = [
8
+ {
9
+ id: 'Pulse',
10
+ label: 'Pulse',
11
+ description: 'Infrastructure health, operational signals & uptime monitoring. Always on.',
12
+ required: true,
13
+ defaultEnabled: true,
14
+ persistedName: 'pulse',
15
+ },
16
+ {
17
+ id: 'Insight',
18
+ label: 'Insight',
19
+ description: 'Cloud asset inventory, misconfiguration detection, CIS checks & topology.',
20
+ required: false,
21
+ defaultEnabled: true,
22
+ persistedName: 'insight',
23
+ },
24
+ {
25
+ id: 'Cost',
26
+ label: 'Cost',
27
+ description: 'Spend anomalies, budgets, and AWS Cost & Usage reporting.',
28
+ required: false,
29
+ defaultEnabled: false,
30
+ persistedName: 'cost',
31
+ },
32
+ {
33
+ id: 'Access',
34
+ label: 'Access',
35
+ description: 'Core access-management capability.',
36
+ required: true,
37
+ defaultEnabled: true,
38
+ visible: false,
39
+ },
40
+ ];
41
+ function normalizeMeyiConnectOnboardingPlugins(plugins) {
42
+ const requested = Array.isArray(plugins)
43
+ ? plugins.filter((plugin) => typeof plugin === 'string')
44
+ : [];
45
+ const allowed = new Set([...exports.meyiConnectPlugins.map((plugin) => plugin.id), 'Security']);
46
+ return [...new Set([...requested, 'Pulse'])].filter((plugin) => allowed.has(plugin));
47
+ }
48
+ function normalizeMeyiConnectPersistedPlugins(plugins) {
49
+ const requested = Array.isArray(plugins)
50
+ ? plugins.filter((plugin) => typeof plugin === 'string')
51
+ : [];
52
+ const normalized = new Set(requested.map((plugin) => {
53
+ const lower = plugin.toLowerCase();
54
+ return lower === 'security' ? 'insight' : lower;
55
+ }));
56
+ normalized.add('pulse');
57
+ return exports.meyiConnectPlugins
58
+ .flatMap((plugin) => plugin.persistedName ? [plugin.persistedName] : [])
59
+ .filter((plugin) => normalized.has(plugin));
60
+ }
61
+ function normalizeMeyiConnectPlugins(plugins) {
62
+ const requested = Array.isArray(plugins)
63
+ ? plugins.filter((plugin) => typeof plugin === 'string')
64
+ : [];
65
+ const expanded = requested.flatMap((plugin) => plugin.toLowerCase() === 'insight' ? ['Insight', 'Security'] : [plugin]);
66
+ const required = exports.meyiConnectPlugins.filter((plugin) => plugin.required).map((plugin) => plugin.id);
67
+ const allowed = new Set([...exports.meyiConnectPlugins.map((plugin) => plugin.id), 'Security']);
68
+ return [...new Set([...expanded, ...required])].filter((plugin) => allowed.has(plugin));
69
+ }
70
+ //# sourceMappingURL=plugin-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-config.js","sourceRoot":"","sources":["../../../src/applications/meyi-connect/plugin-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ProvisionTenantResult } from '../types';
2
+ export declare function syncMeyiConnectTenantPlugins(tenantId: string, plugins: string[]): Promise<void>;
3
+ export declare function provisionMeyiConnectTenant(onboardingId: string): Promise<ProvisionTenantResult>;
4
+ //# sourceMappingURL=provisioner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provisioner.d.ts","sourceRoot":"","sources":["../../../src/applications/meyi-connect/provisioner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGtD,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBrG;AAED,wBAAsB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqGrG"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncMeyiConnectTenantPlugins = syncMeyiConnectTenantPlugins;
4
+ exports.provisionMeyiConnectTenant = provisionMeyiConnectTenant;
5
+ const drizzle_orm_1 = require("drizzle-orm");
6
+ const db_1 = require("../../db");
7
+ const schema_1 = require("../../db/schema");
8
+ const utils_1 = require("../../modules/marketplace/utils");
9
+ const plugin_config_1 = require("./plugin-config");
10
+ async function syncMeyiConnectTenantPlugins(tenantId, plugins) {
11
+ const selected = new Set((0, plugin_config_1.normalizeMeyiConnectPersistedPlugins)(plugins));
12
+ const db = (0, db_1.getDb)();
13
+ for (const plugin of plugin_config_1.meyiConnectPlugins.filter((candidate) => candidate.persistedName)) {
14
+ const pluginName = plugin.persistedName;
15
+ const isEnabled = selected.has(pluginName);
16
+ await db.execute((0, drizzle_orm_1.sql) `
17
+ INSERT INTO meyiconnect.plugins
18
+ (tenant_id, name, is_installed, is_enabled, is_entitled, provisioned_by, created_at, updated_at)
19
+ VALUES
20
+ (${tenantId}::uuid, ${pluginName}, ${isEnabled}, ${isEnabled}, true, 'marketplace', now(), now())
21
+ ON CONFLICT (tenant_id, name) DO UPDATE SET
22
+ is_installed = EXCLUDED.is_installed,
23
+ is_enabled = EXCLUDED.is_enabled,
24
+ is_entitled = EXCLUDED.is_entitled,
25
+ provisioned_by = EXCLUDED.provisioned_by,
26
+ updated_at = now()
27
+ `);
28
+ }
29
+ }
30
+ async function provisionMeyiConnectTenant(onboardingId) {
31
+ const db = (0, db_1.getDb)();
32
+ const sessions = await db
33
+ .select()
34
+ .from(schema_1.onboardingDetails)
35
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.onboardingDetails.id, onboardingId), (0, drizzle_orm_1.eq)(schema_1.onboardingDetails.applicationId, 'meyi-connect')))
36
+ .limit(1);
37
+ const session = sessions[0];
38
+ if (!session)
39
+ throw new Error('Meyi Connect onboarding session not found');
40
+ const workspaceName = session.workspaceName || 'acme';
41
+ const companyName = session.companyName || 'Acme Inc';
42
+ const email = session.email || `admin@${workspaceName.toLowerCase().replace(/[^a-z0-9]/g, '')}.com`;
43
+ const alias = workspaceName.toLowerCase().replace(/[^a-z0-9]/g, '-');
44
+ const passwordHash = session.permanentPasswordHash || '';
45
+ if (!passwordHash)
46
+ throw new Error('Password not found for this onboarding session.');
47
+ const userLookup = await db.execute((0, drizzle_orm_1.sql) `
48
+ SELECT id, password_hash FROM meyiconnect.users WHERE lower(email) = lower(${email}) LIMIT 1
49
+ `);
50
+ let userId = userLookup.rows[0]?.id || '';
51
+ const hadPassword = Boolean(userLookup.rows[0]?.password_hash);
52
+ if (!userId) {
53
+ const createdUser = await db.execute((0, drizzle_orm_1.sql) `
54
+ INSERT INTO meyiconnect.users (email, password_hash, full_name, role, is_active, created_at, updated_at, is_confirmed)
55
+ VALUES (${email}, ${passwordHash}, 'Admin', 'admin', true, now(), now(), true)
56
+ RETURNING id
57
+ `);
58
+ userId = createdUser.rows[0].id;
59
+ }
60
+ const customers = session.awsCustomerIdentifier
61
+ ? await db
62
+ .select({ awsAccountId: schema_1.marketplaceCustomers.awsAccountId })
63
+ .from(schema_1.marketplaceCustomers)
64
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.marketplaceCustomers.applicationId, session.applicationId), (0, drizzle_orm_1.eq)(schema_1.marketplaceCustomers.awsCustomerIdentifier, session.awsCustomerIdentifier)))
65
+ .limit(1)
66
+ : [];
67
+ let tenantId = session.tenantId || '';
68
+ const subscriberAccountId = customers[0]?.awsAccountId || null;
69
+ if (!tenantId) {
70
+ const owned = await db.execute((0, drizzle_orm_1.sql) `
71
+ SELECT id FROM meyiconnect.tenants
72
+ WHERE owner_user_id = ${userId}
73
+ AND lower(name) = lower(${companyName})
74
+ AND lower(coalesce(alias, '')) = lower(${alias})
75
+ AND coalesce(aws_account_id, '') = coalesce(${subscriberAccountId}::text, '')
76
+ LIMIT 1
77
+ `);
78
+ tenantId = owned.rows[0]?.id || '';
79
+ }
80
+ if (!tenantId) {
81
+ const created = await db.execute((0, drizzle_orm_1.sql) `
82
+ INSERT INTO meyiconnect.tenants (alias, name, owner_user_id, aws_account_id, is_msp_managed, is_default, is_active, created_at, updated_at)
83
+ VALUES (${alias}, ${companyName}, ${userId}, ${subscriberAccountId}, true, false, true, now(), now())
84
+ RETURNING id
85
+ `);
86
+ tenantId = created.rows[0].id;
87
+ }
88
+ await db.update(schema_1.onboardingDetails).set({ tenantId, updatedAt: new Date() }).where((0, drizzle_orm_1.eq)(schema_1.onboardingDetails.id, onboardingId));
89
+ await db.execute((0, drizzle_orm_1.sql) `
90
+ UPDATE meyiconnect.users
91
+ SET tenant_id = ${tenantId},
92
+ password_hash = CASE WHEN ${hadPassword} THEN password_hash ELSE ${passwordHash} END,
93
+ is_confirmed = true,
94
+ updated_at = now()
95
+ WHERE id = ${userId}
96
+ `);
97
+ await db.execute((0, drizzle_orm_1.sql) `
98
+ INSERT INTO meyiconnect.user_tenants (user_id, tenant_id, role, is_default)
99
+ VALUES (${userId}, ${tenantId}, 'admin',
100
+ NOT EXISTS (SELECT 1 FROM meyiconnect.user_tenants WHERE user_id = ${userId}))
101
+ ON CONFLICT (user_id, tenant_id) DO NOTHING
102
+ `);
103
+ try {
104
+ await syncMeyiConnectTenantPlugins(tenantId, Array.isArray(session.plugins) && session.plugins.length > 0 ? session.plugins : ['Pulse', 'Insight']);
105
+ }
106
+ catch (error) {
107
+ console.error(`[marketplace:meyi-connect] Failed to provision plugins for tenant ${tenantId}:`, error);
108
+ }
109
+ console.info(`[marketplace:meyi-connect] Provisioned tenant tenantId=${tenantId}, email=${email}.`);
110
+ return {
111
+ tenantId,
112
+ workspaceName,
113
+ companyName,
114
+ email,
115
+ tempPassword: '',
116
+ isNewTenant: true,
117
+ loginUrl: (0, utils_1.appSignInUrl)({ provisioned: '1', email }),
118
+ };
119
+ }
120
+ //# sourceMappingURL=provisioner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provisioner.js","sourceRoot":"","sources":["../../../src/applications/meyi-connect/provisioner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { MarketplaceApplication } from './types';
2
+ export declare const DEFAULT_APPLICATION_ID = "meyi-connect";
3
+ export declare function listApplications(): MarketplaceApplication[];
4
+ export declare function getApplicationById(id: string | null | undefined): MarketplaceApplication | undefined;
5
+ export declare function requireApplicationById(id: string | null | undefined): MarketplaceApplication;
6
+ export declare function getApplicationByProductCode(productCode: string): MarketplaceApplication | undefined;
7
+ export declare function requireApplicationByProductCode(productCode: string): MarketplaceApplication;
8
+ export declare function getDefaultApplication(): MarketplaceApplication;
9
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/applications/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEtD,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AAErD,wBAAgB,gBAAgB,IAAI,sBAAsB,EAAE,CAE3D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,sBAAsB,GAAG,SAAS,CAGpG;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,sBAAsB,CAI5F;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAInG;AAED,wBAAgB,+BAA+B,CAAC,WAAW,EAAE,MAAM,GAAG,sBAAsB,CAI3F;AAED,wBAAgB,qBAAqB,IAAI,sBAAsB,CAG9D"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_APPLICATION_ID = void 0;
4
+ exports.listApplications = listApplications;
5
+ exports.getApplicationById = getApplicationById;
6
+ exports.requireApplicationById = requireApplicationById;
7
+ exports.getApplicationByProductCode = getApplicationByProductCode;
8
+ exports.requireApplicationByProductCode = requireApplicationByProductCode;
9
+ exports.getDefaultApplication = getDefaultApplication;
10
+ const config_1 = require("./meyi-connect/config");
11
+ exports.DEFAULT_APPLICATION_ID = 'meyi-connect';
12
+ function listApplications() {
13
+ return [(0, config_1.createMeyiConnectApplication)()];
14
+ }
15
+ function getApplicationById(id) {
16
+ const applicationId = id?.trim() || exports.DEFAULT_APPLICATION_ID;
17
+ return listApplications().find((application) => application.id === applicationId);
18
+ }
19
+ function requireApplicationById(id) {
20
+ const application = getApplicationById(id);
21
+ if (!application)
22
+ throw new Error(`Unsupported marketplace application: ${id}`);
23
+ return application;
24
+ }
25
+ function getApplicationByProductCode(productCode) {
26
+ return listApplications().find((application) => application.productCode !== '' && application.productCode === productCode);
27
+ }
28
+ function requireApplicationByProductCode(productCode) {
29
+ const application = getApplicationByProductCode(productCode);
30
+ if (!application)
31
+ throw new Error('Registration token does not belong to a supported Marketplace product');
32
+ return application;
33
+ }
34
+ function getDefaultApplication() {
35
+ const configuredId = process.env.MARKETPLACE_DEFAULT_APPLICATION_ID || exports.DEFAULT_APPLICATION_ID;
36
+ return requireApplicationById(configuredId);
37
+ }
38
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/applications/registry.ts"],"names":[],"mappings":";;;AAKA,4CAEC;AAED,gDAGC;AAED,wDAIC;AAED,kEAIC;AAED,0EAIC;AAED,sDAGC;AAnCD,kDAAqE;AAGxD,QAAA,sBAAsB,GAAG,cAAc,CAAC;AAErD,SAAgB,gBAAgB;IAC9B,OAAO,CAAC,IAAA,qCAA4B,GAAE,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,kBAAkB,CAAC,EAA6B;IAC9D,MAAM,aAAa,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,8BAAsB,CAAC;IAC3D,OAAO,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,KAAK,aAAa,CAAC,CAAC;AACpF,CAAC;AAED,SAAgB,sBAAsB,CAAC,EAA6B;IAClE,MAAM,WAAW,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,EAAE,EAAE,CAAC,CAAC;IAChF,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAgB,2BAA2B,CAAC,WAAmB;IAC7D,OAAO,gBAAgB,EAAE,CAAC,IAAI,CAC5B,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,WAAW,KAAK,EAAE,IAAI,WAAW,CAAC,WAAW,KAAK,WAAW,CAC3F,CAAC;AACJ,CAAC;AAED,SAAgB,+BAA+B,CAAC,WAAmB;IACjE,MAAM,WAAW,GAAG,2BAA2B,CAAC,WAAW,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC3G,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAgB,qBAAqB;IACnC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,kCAAkC,IAAI,8BAAsB,CAAC;IAC9F,OAAO,sBAAsB,CAAC,YAAY,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,57 @@
1
+ export interface ApplicationPlugin {
2
+ id: string;
3
+ label: string;
4
+ description: string;
5
+ required: boolean;
6
+ defaultEnabled: boolean;
7
+ visible?: boolean;
8
+ persistedName?: string;
9
+ }
10
+ export interface ApplicationAwsConfig {
11
+ defaultRoleName: string;
12
+ stackSetPrefix: string;
13
+ downloadFilePrefix: string;
14
+ managedByTag: string;
15
+ templatePath: string;
16
+ sessionNamePrefix: string;
17
+ }
18
+ export interface ProvisionTenantResult {
19
+ tenantId: string;
20
+ workspaceName: string;
21
+ companyName: string;
22
+ email: string;
23
+ tempPassword?: string;
24
+ isNewTenant: boolean;
25
+ loginUrl: string;
26
+ }
27
+ export interface PublicApplicationConfig {
28
+ id: string;
29
+ name: string;
30
+ productId: string;
31
+ loginUrl: string;
32
+ dashboardUrl: string;
33
+ awsRoleName: string;
34
+ downloadFilePrefix: string;
35
+ features: {
36
+ awsOrganization: boolean;
37
+ };
38
+ plugins: ApplicationPlugin[];
39
+ }
40
+ export interface MarketplaceApplication {
41
+ id: string;
42
+ name: string;
43
+ productCode: string;
44
+ productId: string;
45
+ databaseSchema: string;
46
+ aws: ApplicationAwsConfig;
47
+ plugins: ApplicationPlugin[];
48
+ normalizeOnboardingPlugins(plugins: unknown): string[];
49
+ normalizePlugins(plugins: unknown): string[];
50
+ provisionTenant(onboardingId: string): Promise<ProvisionTenantResult>;
51
+ syncTenantPlugins(tenantId: string, plugins: string[]): Promise<void>;
52
+ signInUrl(params?: Record<string, string>): string;
53
+ onboardingCompleteUrl(workspaceName: string): string;
54
+ dashboardUrl(): string;
55
+ toPublicConfig(): PublicApplicationConfig;
56
+ }
57
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/applications/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE;QACR,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,oBAAoB,CAAC;IAC1B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;IACvD,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;IAC7C,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACtE,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;IACnD,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;IACrD,YAAY,IAAI,MAAM,CAAC;IACvB,cAAc,IAAI,uBAAuB,CAAC;CAC3C"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/applications/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * db/index.ts — Singleton Drizzle database client.
3
+ *
4
+ * Security:
5
+ * - DATABASE_URL loaded exclusively from environment (no fallback literal).
6
+ * - Pool sized conservatively to avoid overwhelming the shared PostgreSQL instance.
7
+ * - mTLS respected if present in the connection string.
8
+ */
9
+ import { NodePgDatabase } from 'drizzle-orm/node-postgres';
10
+ import * as schema from './schema';
11
+ export declare function getDb(): NodePgDatabase<typeof schema>;
12
+ export declare function closeDb(): Promise<void>;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAW,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAKnC,wBAAgB,KAAK,IAAI,cAAc,CAAC,OAAO,MAAM,CAAC,CAmBrD;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAM7C"}