@optimizely/ocp-local-env 1.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -0
- package/dist/package.json +104 -0
- package/dist/public/bundle.da978bb5437cd82e6d37.js +3 -0
- package/dist/public/bundle.da978bb5437cd82e6d37.js.LICENSE.txt +49 -0
- package/dist/public/bundle.da978bb5437cd82e6d37.js.map +1 -0
- package/dist/public/index.html +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +88 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/executor/FunctionExecutor.d.ts +56 -0
- package/dist/src/executor/FunctionExecutor.js +175 -0
- package/dist/src/executor/FunctionExecutor.js.map +1 -0
- package/dist/src/executor/JobExecutor.d.ts +60 -0
- package/dist/src/executor/JobExecutor.js +203 -0
- package/dist/src/executor/JobExecutor.js.map +1 -0
- package/dist/src/executor/LifecycleExecutor.d.ts +45 -0
- package/dist/src/executor/LifecycleExecutor.js +153 -0
- package/dist/src/executor/LifecycleExecutor.js.map +1 -0
- package/dist/src/executor/watcher.d.ts +63 -0
- package/dist/src/executor/watcher.js +213 -0
- package/dist/src/executor/watcher.js.map +1 -0
- package/dist/src/functions/hello.d.ts +4 -0
- package/dist/src/functions/hello.js +8 -0
- package/dist/src/functions/hello.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +9 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/jobs/dailyJob.d.ts +4 -0
- package/dist/src/jobs/dailyJob.js +8 -0
- package/dist/src/jobs/dailyJob.js.map +1 -0
- package/dist/src/local_engine/LocalNotifier.d.ts +10 -0
- package/dist/src/local_engine/LocalNotifier.js +26 -0
- package/dist/src/local_engine/LocalNotifier.js.map +1 -0
- package/dist/src/local_engine/local-engine-child-base.d.ts +79 -0
- package/dist/src/local_engine/local-engine-child-base.js +304 -0
- package/dist/src/local_engine/local-engine-child-base.js.map +1 -0
- package/dist/src/local_engine/local-engine-client.d.ts +80 -0
- package/dist/src/local_engine/local-engine-client.js +333 -0
- package/dist/src/local_engine/local-engine-client.js.map +1 -0
- package/dist/src/local_engine/local-engine-types.d.ts +132 -0
- package/dist/src/local_engine/local-engine-types.js +6 -0
- package/dist/src/local_engine/local-engine-types.js.map +1 -0
- package/dist/src/local_engine/local-engine-unified.d.ts +40 -0
- package/dist/src/local_engine/local-engine-unified.js +406 -0
- package/dist/src/local_engine/local-engine-unified.js.map +1 -0
- package/dist/src/local_engine/local-engine-utils.d.ts +70 -0
- package/dist/src/local_engine/local-engine-utils.js +192 -0
- package/dist/src/local_engine/local-engine-utils.js.map +1 -0
- package/dist/src/local_engine/localSDKConfig.d.ts +30 -0
- package/dist/src/local_engine/localSDKConfig.js +392 -0
- package/dist/src/local_engine/localSDKConfig.js.map +1 -0
- package/dist/src/local_engine/storage/LocalConfigStore.d.ts +56 -0
- package/dist/src/local_engine/storage/LocalConfigStore.js +129 -0
- package/dist/src/local_engine/storage/LocalConfigStore.js.map +1 -0
- package/dist/src/local_engine/storage/LocalJobStore.d.ts +110 -0
- package/dist/src/local_engine/storage/LocalJobStore.js +239 -0
- package/dist/src/local_engine/storage/LocalJobStore.js.map +1 -0
- package/dist/src/local_engine/storage/LocalKVStore.d.ts +105 -0
- package/dist/src/local_engine/storage/LocalKVStore.js +1002 -0
- package/dist/src/local_engine/storage/LocalKVStore.js.map +1 -0
- package/dist/src/local_engine/storage/LocalNotificationStore.d.ts +27 -0
- package/dist/src/local_engine/storage/LocalNotificationStore.js +125 -0
- package/dist/src/local_engine/storage/LocalNotificationStore.js.map +1 -0
- package/dist/src/local_engine/storage/LocalSecretsStore.d.ts +114 -0
- package/dist/src/local_engine/storage/LocalSecretsStore.js +319 -0
- package/dist/src/local_engine/storage/LocalSecretsStore.js.map +1 -0
- package/dist/src/local_engine/storage/LocalSettingsStore.d.ts +161 -0
- package/dist/src/local_engine/storage/LocalSettingsStore.js +417 -0
- package/dist/src/local_engine/storage/LocalSettingsStore.js.map +1 -0
- package/dist/src/local_engine/storage/NumberSet.d.ts +21 -0
- package/dist/src/local_engine/storage/NumberSet.js +32 -0
- package/dist/src/local_engine/storage/NumberSet.js.map +1 -0
- package/dist/src/local_engine/storage/StringSet.d.ts +21 -0
- package/dist/src/local_engine/storage/StringSet.js +32 -0
- package/dist/src/local_engine/storage/StringSet.js.map +1 -0
- package/dist/src/local_engine/types.d.ts +52 -0
- package/dist/src/local_engine/types.js +6 -0
- package/dist/src/local_engine/types.js.map +1 -0
- package/dist/src/local_engine/utils.d.ts +31 -0
- package/dist/src/local_engine/utils.js +126 -0
- package/dist/src/local_engine/utils.js.map +1 -0
- package/dist/src/logging/LogManager.d.ts +89 -0
- package/dist/src/logging/LogManager.js +237 -0
- package/dist/src/logging/LogManager.js.map +1 -0
- package/dist/src/server/api/functions.d.ts +7 -0
- package/dist/src/server/api/functions.js +80 -0
- package/dist/src/server/api/functions.js.map +1 -0
- package/dist/src/server/api/jobs.d.ts +8 -0
- package/dist/src/server/api/jobs.js +242 -0
- package/dist/src/server/api/jobs.js.map +1 -0
- package/dist/src/server/api/lifecycle.d.ts +6 -0
- package/dist/src/server/api/lifecycle.js +73 -0
- package/dist/src/server/api/lifecycle.js.map +1 -0
- package/dist/src/server/api/settings.d.ts +6 -0
- package/dist/src/server/api/settings.js +117 -0
- package/dist/src/server/api/settings.js.map +1 -0
- package/dist/src/server/api/stores.d.ts +2 -0
- package/dist/src/server/api/stores.js +341 -0
- package/dist/src/server/api/stores.js.map +1 -0
- package/dist/src/server/api/v1.d.ts +10 -0
- package/dist/src/server/api/v1.js +711 -0
- package/dist/src/server/api/v1.js.map +1 -0
- package/dist/src/server/api.d.ts +8 -0
- package/dist/src/server/api.js +154 -0
- package/dist/src/server/api.js.map +1 -0
- package/dist/src/server/app-discovery.d.ts +5 -0
- package/dist/src/server/app-discovery.js +81 -0
- package/dist/src/server/app-discovery.js.map +1 -0
- package/dist/src/server/config.d.ts +21 -0
- package/dist/src/server/config.js +100 -0
- package/dist/src/server/config.js.map +1 -0
- package/dist/src/server/websocket.d.ts +0 -0
- package/dist/src/server/websocket.js +2 -0
- package/dist/src/server/websocket.js.map +1 -0
- package/dist/src/server.d.ts +2 -0
- package/dist/src/server.js +546 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/types/app.d.ts +155 -0
- package/dist/src/types/app.js +24 -0
- package/dist/src/types/app.js.map +1 -0
- package/dist/src/types/kvstore.d.ts +320 -0
- package/dist/src/types/kvstore.js +5 -0
- package/dist/src/types/kvstore.js.map +1 -0
- package/dist/src/ui/components/App.d.ts +6 -0
- package/dist/src/ui/components/App.js +255 -0
- package/dist/src/ui/components/App.js.map +1 -0
- package/dist/src/ui/components/FunctionsView.d.ts +6 -0
- package/dist/src/ui/components/FunctionsView.js +217 -0
- package/dist/src/ui/components/FunctionsView.js.map +1 -0
- package/dist/src/ui/components/JobsView.d.ts +6 -0
- package/dist/src/ui/components/JobsView.js +257 -0
- package/dist/src/ui/components/JobsView.js.map +1 -0
- package/dist/src/ui/components/KVStoreViewer.d.ts +11 -0
- package/dist/src/ui/components/KVStoreViewer.js +168 -0
- package/dist/src/ui/components/KVStoreViewer.js.map +1 -0
- package/dist/src/ui/components/NotificationViewer.d.ts +16 -0
- package/dist/src/ui/components/NotificationViewer.js +69 -0
- package/dist/src/ui/components/NotificationViewer.js.map +1 -0
- package/dist/src/ui/components/SecretsStoreViewer.d.ts +11 -0
- package/dist/src/ui/components/SecretsStoreViewer.js +179 -0
- package/dist/src/ui/components/SecretsStoreViewer.js.map +1 -0
- package/dist/src/ui/components/SettingsStoreViewer.d.ts +24 -0
- package/dist/src/ui/components/SettingsStoreViewer.js +132 -0
- package/dist/src/ui/components/SettingsStoreViewer.js.map +1 -0
- package/dist/src/ui/components/StoreViewer.d.ts +16 -0
- package/dist/src/ui/components/StoreViewer.js +86 -0
- package/dist/src/ui/components/StoreViewer.js.map +1 -0
- package/dist/src/ui/components/TabbedConsole.d.ts +15 -0
- package/dist/src/ui/components/TabbedConsole.js +93 -0
- package/dist/src/ui/components/TabbedConsole.js.map +1 -0
- package/dist/src/ui/components/common/DataTree.d.ts +15 -0
- package/dist/src/ui/components/common/DataTree.js +95 -0
- package/dist/src/ui/components/common/DataTree.js.map +1 -0
- package/dist/src/ui/components/common/EyeIcon.d.ts +11 -0
- package/dist/src/ui/components/common/EyeIcon.js +11 -0
- package/dist/src/ui/components/common/EyeIcon.js.map +1 -0
- package/dist/src/ui/index.d.ts +1 -0
- package/dist/src/ui/index.js +20 -0
- package/dist/src/ui/index.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,711 @@
|
|
|
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.createV1Routes = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
/**
|
|
9
|
+
* Create v1 API routes matching the REST API documentation
|
|
10
|
+
*/
|
|
11
|
+
function createV1Routes(app, watcher, configStore, lifecycleExecutor, settingsStore) {
|
|
12
|
+
const router = express_1.default.Router();
|
|
13
|
+
// Import individual v1 route modules
|
|
14
|
+
router.use('/apps', createAppsRoutes(app, watcher, configStore));
|
|
15
|
+
router.use("/accounts", createAccountsRoutes(app, watcher, configStore, lifecycleExecutor, settingsStore));
|
|
16
|
+
router.use('/installs', createInstallsRoutes(app, watcher, configStore, settingsStore, lifecycleExecutor));
|
|
17
|
+
// Data syncs endpoint
|
|
18
|
+
router.get('/data-syncs', (req, res) => {
|
|
19
|
+
// Accept query parameters but don't use them for now
|
|
20
|
+
const { tracker_id, sort_field, sort_direction, offset, limit } = req.query;
|
|
21
|
+
// Return empty array as requested
|
|
22
|
+
res.json({
|
|
23
|
+
dataSyncItems: []
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
return router;
|
|
27
|
+
}
|
|
28
|
+
exports.createV1Routes = createV1Routes;
|
|
29
|
+
/**
|
|
30
|
+
* Create apps-specific routes for /v1/apps
|
|
31
|
+
*/
|
|
32
|
+
function createAppsRoutes(app, watcher, configStore) {
|
|
33
|
+
const router = express_1.default.Router();
|
|
34
|
+
/**
|
|
35
|
+
* GET /v1/apps
|
|
36
|
+
* Lists running app versions
|
|
37
|
+
*/
|
|
38
|
+
router.get('/', (req, res) => {
|
|
39
|
+
const { tracker_id, include_manifest_json, categories, installed } = req.query;
|
|
40
|
+
// Get server URL from request
|
|
41
|
+
const protocol = req.secure ? 'https' : 'http';
|
|
42
|
+
const host = req.get('host') || 'localhost:3000';
|
|
43
|
+
const serverUrl = `${protocol}://${host}`;
|
|
44
|
+
// Build app metadata
|
|
45
|
+
const metadata = {
|
|
46
|
+
categories: app.manifest.meta.categories || [],
|
|
47
|
+
display_name: app.manifest.meta.display_name,
|
|
48
|
+
summary: app.manifest.meta.summary || '',
|
|
49
|
+
overview_url: `${serverUrl}/assets/overview.md`,
|
|
50
|
+
support_url: app.manifest.meta.support_url || '',
|
|
51
|
+
logo_url: `${serverUrl}/assets/logo.svg`,
|
|
52
|
+
icon_url: `${serverUrl}/assets/icon.svg`,
|
|
53
|
+
vendor: app.manifest.meta.vendor || '',
|
|
54
|
+
contact_email: app.manifest.meta.contact_email || '',
|
|
55
|
+
settings_url: `${serverUrl}/assets/settings.yml`,
|
|
56
|
+
content_settings_url: '',
|
|
57
|
+
content_template_url: '' // Not available in local app manifest
|
|
58
|
+
};
|
|
59
|
+
const now = new Date().toISOString();
|
|
60
|
+
// Check installation status from config store
|
|
61
|
+
const isInstalled = configStore.isInstalled();
|
|
62
|
+
// Build the app result
|
|
63
|
+
const appResult = {
|
|
64
|
+
id: {
|
|
65
|
+
app_id: app.manifest.meta.app_id,
|
|
66
|
+
version: app.manifest.meta.version
|
|
67
|
+
},
|
|
68
|
+
metadata: metadata,
|
|
69
|
+
installed: isInstalled,
|
|
70
|
+
installed_at: isInstalled ? now : undefined,
|
|
71
|
+
created_at: now,
|
|
72
|
+
updated_at: now,
|
|
73
|
+
installed_version: isInstalled ? app.manifest.meta.version : undefined,
|
|
74
|
+
manifest_json: include_manifest_json === 'true' ? JSON.stringify(app.manifest) : '',
|
|
75
|
+
install_id: isInstalled ? 1 : undefined // Static install ID for local development
|
|
76
|
+
};
|
|
77
|
+
// Apply filters if provided
|
|
78
|
+
let results = [appResult];
|
|
79
|
+
// Filter by categories if specified
|
|
80
|
+
if (categories) {
|
|
81
|
+
const categoryFilters = Array.isArray(categories) ? categories : [categories];
|
|
82
|
+
const appCategories = app.manifest.meta.categories || [];
|
|
83
|
+
const hasMatchingCategory = categoryFilters.some(cat => appCategories.includes(cat));
|
|
84
|
+
if (!hasMatchingCategory) {
|
|
85
|
+
results = [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Filter by installed status if specified
|
|
89
|
+
if (installed !== undefined) {
|
|
90
|
+
const isInstalledFilter = installed === 'true';
|
|
91
|
+
if (isInstalledFilter !== isInstalled) {
|
|
92
|
+
results = [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Return the results
|
|
96
|
+
res.json({
|
|
97
|
+
results: results
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* GET /v1/apps/{app_id}
|
|
102
|
+
* Fetches details of a specific app
|
|
103
|
+
*/
|
|
104
|
+
router.get('/:app_id', (req, res) => {
|
|
105
|
+
const { app_id } = req.params;
|
|
106
|
+
const { tracker_id } = req.query;
|
|
107
|
+
// Get server URL from request
|
|
108
|
+
const protocol = req.secure ? 'https' : 'http';
|
|
109
|
+
const host = req.get('host') || 'localhost:3000';
|
|
110
|
+
const serverUrl = `${protocol}://${host}`;
|
|
111
|
+
// Check if the requested app_id matches our local app
|
|
112
|
+
if (app_id !== app.manifest.meta.app_id) {
|
|
113
|
+
return res.status(404).json({
|
|
114
|
+
error: {
|
|
115
|
+
message: `App with id '${app_id}' not found`,
|
|
116
|
+
code: 'APP_NOT_FOUND'
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Build app version metadata from manifest
|
|
121
|
+
const metadata = {
|
|
122
|
+
categories: app.manifest.meta.categories || [],
|
|
123
|
+
display_name: app.manifest.meta.display_name,
|
|
124
|
+
summary: app.manifest.meta.summary || '',
|
|
125
|
+
overview_url: `${serverUrl}/assets/overview.md`,
|
|
126
|
+
support_url: app.manifest.meta.support_url || '',
|
|
127
|
+
logo_url: `${serverUrl}/assets/logo.svg`,
|
|
128
|
+
icon_url: `${serverUrl}/assets/icon.svg`,
|
|
129
|
+
vendor: app.manifest.meta.vendor || '',
|
|
130
|
+
contact_email: app.manifest.meta.contact_email || '',
|
|
131
|
+
settings_url: `${serverUrl}/assets/settings.yml`,
|
|
132
|
+
content_settings_url: '',
|
|
133
|
+
content_template_url: '' // Not available in local app manifest
|
|
134
|
+
};
|
|
135
|
+
const now = new Date().toISOString();
|
|
136
|
+
// Check installation status from config store
|
|
137
|
+
const isInstalled = configStore.isInstalled();
|
|
138
|
+
// Return app details according to the API specification
|
|
139
|
+
const response = {
|
|
140
|
+
app: {
|
|
141
|
+
id: {
|
|
142
|
+
app_id: app.manifest.meta.app_id,
|
|
143
|
+
version: app.manifest.meta.version
|
|
144
|
+
},
|
|
145
|
+
metadata: metadata,
|
|
146
|
+
installed: isInstalled,
|
|
147
|
+
installed_at: isInstalled ? now : undefined,
|
|
148
|
+
installed_version: isInstalled ? app.manifest.meta.version : undefined,
|
|
149
|
+
created_at: now,
|
|
150
|
+
updated_at: now
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
res.json(response);
|
|
154
|
+
});
|
|
155
|
+
/**
|
|
156
|
+
* GET /v1/apps/{app_id}/versions/{version}
|
|
157
|
+
* Fetches details of a specific app version
|
|
158
|
+
*/
|
|
159
|
+
router.get('/:app_id/versions/:version', (req, res) => {
|
|
160
|
+
const { app_id, version } = req.params;
|
|
161
|
+
// Get server URL from request
|
|
162
|
+
const protocol = req.secure ? 'https' : 'http';
|
|
163
|
+
const host = req.get('host') || 'localhost:3000';
|
|
164
|
+
const serverUrl = `${protocol}://${host}`;
|
|
165
|
+
// Check if the requested app_id and version match our local app
|
|
166
|
+
if (app_id !== app.manifest.meta.app_id || version !== app.manifest.meta.version) {
|
|
167
|
+
return res.status(404).json({
|
|
168
|
+
error: {
|
|
169
|
+
message: `App version '${app_id}@${version}' not found`,
|
|
170
|
+
code: 'APP_VERSION_NOT_FOUND'
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// Build app version metadata from manifest
|
|
175
|
+
const metadata = {
|
|
176
|
+
categories: app.manifest.meta.categories || [],
|
|
177
|
+
display_name: app.manifest.meta.display_name,
|
|
178
|
+
summary: app.manifest.meta.summary || '',
|
|
179
|
+
overview_url: `${serverUrl}/assets/overview.md`,
|
|
180
|
+
support_url: app.manifest.meta.support_url || '',
|
|
181
|
+
logo_url: `${serverUrl}/assets/logo.svg`,
|
|
182
|
+
icon_url: `${serverUrl}/assets/icon.svg`,
|
|
183
|
+
vendor: app.manifest.meta.vendor || '',
|
|
184
|
+
contact_email: app.manifest.meta.contact_email || '',
|
|
185
|
+
settings_url: `${serverUrl}/assets/settings.yml`,
|
|
186
|
+
content_settings_url: '',
|
|
187
|
+
content_template_url: '' // Not available in local app manifest
|
|
188
|
+
};
|
|
189
|
+
const now = new Date().toISOString();
|
|
190
|
+
// Build channel information if available
|
|
191
|
+
let channelInfo = null;
|
|
192
|
+
if (app.manifest.channel) {
|
|
193
|
+
channelInfo = {
|
|
194
|
+
type: app.manifest.channel.type,
|
|
195
|
+
options: app.manifest.channel.options || {
|
|
196
|
+
prepare: false,
|
|
197
|
+
template_preview: false
|
|
198
|
+
},
|
|
199
|
+
targeting: app.manifest.channel.targeting === 'dynamic' ?
|
|
200
|
+
{ dynamic: true, targets: [] } :
|
|
201
|
+
{ dynamic: false, targets: [] },
|
|
202
|
+
delivery: {
|
|
203
|
+
batch_size: app.manifest.channel.delivery?.batch_size || 100,
|
|
204
|
+
concurrent_batches: app.manifest.channel.delivery?.concurrent_batches || 5,
|
|
205
|
+
rate_limits: app.manifest.channel.delivery?.rate_limits?.map(limit => ({
|
|
206
|
+
count: limit.count,
|
|
207
|
+
period: limit.period,
|
|
208
|
+
unit: limit.unit === 'minute' ? 2 : limit.unit === 'hour' ? 3 : limit.unit === 'day' ? 4 : 1,
|
|
209
|
+
grouping: limit.grouping === 'app' ? 1 : 2 // Convert to enum
|
|
210
|
+
})) || []
|
|
211
|
+
},
|
|
212
|
+
metrics: {
|
|
213
|
+
delivery: app.manifest.channel.metrics?.delivery || [],
|
|
214
|
+
engagement: app.manifest.channel.metrics?.engagement || [],
|
|
215
|
+
attributable: app.manifest.channel.metrics?.attributable || [],
|
|
216
|
+
disengagement: app.manifest.channel.metrics?.disengagement || [],
|
|
217
|
+
reachability: app.manifest.channel.metrics?.reachability || []
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// Return app version details according to the API specification
|
|
222
|
+
const response = {
|
|
223
|
+
app_version: {
|
|
224
|
+
id: {
|
|
225
|
+
app_id: app.manifest.meta.app_id,
|
|
226
|
+
version: app.manifest.meta.version
|
|
227
|
+
},
|
|
228
|
+
metadata: metadata,
|
|
229
|
+
created_at: now,
|
|
230
|
+
updated_at: now,
|
|
231
|
+
...(channelInfo && { channel: channelInfo })
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
res.json(response);
|
|
235
|
+
});
|
|
236
|
+
return router;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Create accounts-specific routes for /v1/accounts
|
|
240
|
+
*/
|
|
241
|
+
function createAccountsRoutes(app, watcher, configStore, lifecycleExecutor, settingsStore) {
|
|
242
|
+
const router = express_1.default.Router();
|
|
243
|
+
/**
|
|
244
|
+
* GET /v1/accounts/{tracker_id}/installs
|
|
245
|
+
* Lists apps installed in an account
|
|
246
|
+
*/
|
|
247
|
+
router.get("/:tracker_id/installs", (req, res) => {
|
|
248
|
+
const { tracker_id } = req.params;
|
|
249
|
+
const { categories, includes_features } = req.query;
|
|
250
|
+
const now = new Date().toISOString();
|
|
251
|
+
// Check installation status from config store
|
|
252
|
+
const isInstalled = configStore.isInstalled();
|
|
253
|
+
// If not installed, return empty array
|
|
254
|
+
if (!isInstalled) {
|
|
255
|
+
return res.json({
|
|
256
|
+
installations: [],
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
// Build the installation object for the local app
|
|
260
|
+
const installation = {
|
|
261
|
+
id: 1,
|
|
262
|
+
app_id: app.manifest.meta.app_id,
|
|
263
|
+
version: app.manifest.meta.version,
|
|
264
|
+
account_id: 123,
|
|
265
|
+
tracker_id: tracker_id,
|
|
266
|
+
app_instance_id: `${app.manifest.meta.app_id}-local-instance`,
|
|
267
|
+
created_at: now,
|
|
268
|
+
updated_at: now,
|
|
269
|
+
};
|
|
270
|
+
// Apply filters if provided
|
|
271
|
+
let installations = [installation];
|
|
272
|
+
// Filter by categories if specified
|
|
273
|
+
if (categories) {
|
|
274
|
+
const categoryFilters = Array.isArray(categories)
|
|
275
|
+
? categories
|
|
276
|
+
: [categories];
|
|
277
|
+
const appCategories = app.manifest.meta.categories || [];
|
|
278
|
+
const hasMatchingCategory = categoryFilters.some((cat) => appCategories.includes(cat));
|
|
279
|
+
if (!hasMatchingCategory) {
|
|
280
|
+
installations = [];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Filter by features if specified (1=DESTINATION_APP, 2=SOURCE_APP)
|
|
284
|
+
if (includes_features) {
|
|
285
|
+
const featureFilters = Array.isArray(includes_features)
|
|
286
|
+
? includes_features
|
|
287
|
+
: [includes_features];
|
|
288
|
+
const hasDestinations = app.manifest.destinations &&
|
|
289
|
+
Object.keys(app.manifest.destinations).length > 0;
|
|
290
|
+
const hasSources = app.manifest.sources && Object.keys(app.manifest.sources).length > 0;
|
|
291
|
+
const hasMatchingFeature = featureFilters.some((feature) => {
|
|
292
|
+
return ((feature === "1" && hasDestinations) ||
|
|
293
|
+
(feature === "2" && hasSources));
|
|
294
|
+
});
|
|
295
|
+
if (!hasMatchingFeature) {
|
|
296
|
+
installations = [];
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Return the installations
|
|
300
|
+
res.json({
|
|
301
|
+
installations: installations,
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
/**
|
|
305
|
+
* POST /v1/accounts/{tracker_id}/installs
|
|
306
|
+
* Installs an AppVersion to an account
|
|
307
|
+
*/
|
|
308
|
+
router.post("/:tracker_id/installs", async (req, res) => {
|
|
309
|
+
const { tracker_id } = req.params;
|
|
310
|
+
let { app_id, version } = req.body;
|
|
311
|
+
// Validate request body
|
|
312
|
+
if (!app_id) {
|
|
313
|
+
return res.status(400).json({
|
|
314
|
+
error: {
|
|
315
|
+
message: "app_id is required",
|
|
316
|
+
code: "INVALID_REQUEST",
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (!version) {
|
|
321
|
+
version = app.manifest.meta.version;
|
|
322
|
+
}
|
|
323
|
+
// Check if the requested app matches our local app
|
|
324
|
+
if (app_id !== app.manifest.meta.app_id ||
|
|
325
|
+
version !== app.manifest.meta.version) {
|
|
326
|
+
return res.status(404).json({
|
|
327
|
+
error: {
|
|
328
|
+
message: `App version '${app_id}@${version}' not found`,
|
|
329
|
+
code: "APP_VERSION_NOT_FOUND",
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// Check if already installed
|
|
334
|
+
if (configStore.isInstalled()) {
|
|
335
|
+
return res.status(409).json({
|
|
336
|
+
error: {
|
|
337
|
+
message: "App is already installed",
|
|
338
|
+
code: "ALREADY_INSTALLED",
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
// Execute the onInstall lifecycle method
|
|
344
|
+
const lifecycleResult = await lifecycleExecutor.executeInstall();
|
|
345
|
+
if (lifecycleResult.success) {
|
|
346
|
+
// Mark app as installed in config store
|
|
347
|
+
configStore.setInstalled(true);
|
|
348
|
+
const now = new Date().toISOString();
|
|
349
|
+
// Return installation record
|
|
350
|
+
const installation = {
|
|
351
|
+
id: 1,
|
|
352
|
+
app_id: app.manifest.meta.app_id,
|
|
353
|
+
version: app.manifest.meta.version,
|
|
354
|
+
account_id: 123,
|
|
355
|
+
tracker_id: tracker_id,
|
|
356
|
+
app_instance_id: `${app.manifest.meta.app_id}-local-instance`,
|
|
357
|
+
created_at: now,
|
|
358
|
+
updated_at: now,
|
|
359
|
+
};
|
|
360
|
+
res.json({
|
|
361
|
+
app_installation: installation,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// Installation failed - return error with lifecycle logs
|
|
366
|
+
res.status(500).json({
|
|
367
|
+
error: {
|
|
368
|
+
message: "Installation failed during lifecycle execution",
|
|
369
|
+
code: "LIFECYCLE_EXECUTION_FAILED",
|
|
370
|
+
details: {
|
|
371
|
+
logs: lifecycleResult.logs,
|
|
372
|
+
error: lifecycleResult.error,
|
|
373
|
+
executionTime: lifecycleResult.executionTime,
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
// Unexpected error during installation
|
|
381
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
382
|
+
res.status(500).json({
|
|
383
|
+
error: {
|
|
384
|
+
message: `Installation failed: ${errorMessage}`,
|
|
385
|
+
code: "INSTALLATION_ERROR",
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
/**
|
|
391
|
+
* DELETE /v1/accounts/{tracker_id}/installs/{app_id}
|
|
392
|
+
* Uninstalls an AppVersion from an account
|
|
393
|
+
*/
|
|
394
|
+
router.delete("/:tracker_id/installs/:app_id", async (req, res) => {
|
|
395
|
+
const { tracker_id, app_id } = req.params;
|
|
396
|
+
// Check if the requested app matches our local app
|
|
397
|
+
if (app_id !== app.manifest.meta.app_id) {
|
|
398
|
+
return res.status(404).json({
|
|
399
|
+
error: {
|
|
400
|
+
message: `App with id '${app_id}' not found`,
|
|
401
|
+
code: "APP_NOT_FOUND",
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// Check if app is installed
|
|
406
|
+
if (!configStore.isInstalled()) {
|
|
407
|
+
return res.status(404).json({
|
|
408
|
+
error: {
|
|
409
|
+
message: "App is not installed",
|
|
410
|
+
code: "NOT_INSTALLED",
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
// Execute the onUninstall lifecycle method
|
|
416
|
+
const lifecycleResult = await lifecycleExecutor.executeUninstall();
|
|
417
|
+
if (lifecycleResult.success) {
|
|
418
|
+
// Mark app as not installed in config store
|
|
419
|
+
configStore.setInstalled(false);
|
|
420
|
+
// Clear all settings data
|
|
421
|
+
settingsStore.clearSettings();
|
|
422
|
+
// Clear function GUIDs
|
|
423
|
+
configStore.clearFunctionGuids();
|
|
424
|
+
// Return 200 OK as specified in the API documentation
|
|
425
|
+
res.status(200).send({});
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
// Uninstall failed - return error with lifecycle logs
|
|
429
|
+
res.status(500).json({
|
|
430
|
+
error: {
|
|
431
|
+
message: "Uninstall failed during lifecycle execution",
|
|
432
|
+
code: "LIFECYCLE_EXECUTION_FAILED",
|
|
433
|
+
details: {
|
|
434
|
+
logs: lifecycleResult.logs,
|
|
435
|
+
error: lifecycleResult.error,
|
|
436
|
+
executionTime: lifecycleResult.executionTime,
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
// Unexpected error during uninstall
|
|
444
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
445
|
+
res.status(500).json({
|
|
446
|
+
error: {
|
|
447
|
+
message: `Uninstall failed: ${errorMessage}`,
|
|
448
|
+
code: "UNINSTALL_ERROR",
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
return router;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Create installs-specific routes for /v1/installs
|
|
457
|
+
*/
|
|
458
|
+
function createInstallsRoutes(app, watcher, configStore, settingsStore, lifecycleExecutor) {
|
|
459
|
+
const router = express_1.default.Router();
|
|
460
|
+
/**
|
|
461
|
+
* GET /v1/installs/{app_install_id}/settings
|
|
462
|
+
* Gets stored app secrets for a setup form
|
|
463
|
+
*/
|
|
464
|
+
router.get("/:app_install_id/settings", (req, res) => {
|
|
465
|
+
const { app_install_id } = req.params;
|
|
466
|
+
// Validate app_install_id (should be numeric for our implementation)
|
|
467
|
+
const installId = parseInt(app_install_id, 10);
|
|
468
|
+
if (isNaN(installId)) {
|
|
469
|
+
return res.status(400).json({
|
|
470
|
+
error: {
|
|
471
|
+
message: "app_install_id must be a valid number",
|
|
472
|
+
code: "INVALID_INSTALL_ID",
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
// Check if app is installed (for local dev, we use install ID 1)
|
|
477
|
+
if (!configStore.isInstalled()) {
|
|
478
|
+
return res.status(404).json({
|
|
479
|
+
error: {
|
|
480
|
+
message: "App installation not found",
|
|
481
|
+
code: "INSTALLATION_NOT_FOUND",
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
// For local development, we only support install ID 1
|
|
486
|
+
if (installId !== 1) {
|
|
487
|
+
return res.status(404).json({
|
|
488
|
+
error: {
|
|
489
|
+
message: `Installation with ID ${installId} not found`,
|
|
490
|
+
code: "INSTALLATION_NOT_FOUND",
|
|
491
|
+
},
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
// Get sections from settings.yml
|
|
496
|
+
const sections = settingsStore.getSections();
|
|
497
|
+
const formData = {};
|
|
498
|
+
// For each section, get the stored values from local settings store
|
|
499
|
+
sections.forEach((sectionName) => {
|
|
500
|
+
const sectionSettings = settingsStore.getSectionSettings(sectionName);
|
|
501
|
+
formData[sectionName] = sectionSettings;
|
|
502
|
+
});
|
|
503
|
+
// Convert to JSON string
|
|
504
|
+
const formDataJson = JSON.stringify(formData);
|
|
505
|
+
// Return settings according to API specification
|
|
506
|
+
res.json({
|
|
507
|
+
form_data_json: formDataJson,
|
|
508
|
+
error: null, // No error if successful
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
// Return error response
|
|
513
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
514
|
+
res.json({
|
|
515
|
+
form_data_json: "{}",
|
|
516
|
+
error: {
|
|
517
|
+
message: errorMessage,
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
/**
|
|
523
|
+
* POST /v1/installs/{app_install_id}/settings
|
|
524
|
+
* Submits app settings form and calls onSettingsForm lifecycle method
|
|
525
|
+
*/
|
|
526
|
+
router.post("/:app_install_id/settings", async (req, res) => {
|
|
527
|
+
const { app_install_id } = req.params;
|
|
528
|
+
const { form_page, action, form_data_json } = req.body;
|
|
529
|
+
// Validate app_install_id (should be numeric for our implementation)
|
|
530
|
+
const installId = parseInt(app_install_id, 10);
|
|
531
|
+
if (isNaN(installId)) {
|
|
532
|
+
return res.status(400).json({
|
|
533
|
+
error: {
|
|
534
|
+
message: "app_install_id must be a valid number",
|
|
535
|
+
code: "INVALID_INSTALL_ID",
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
// Check if app is installed (for local dev, we use install ID 1)
|
|
540
|
+
if (!configStore.isInstalled()) {
|
|
541
|
+
return res.status(404).json({
|
|
542
|
+
error: {
|
|
543
|
+
message: "App installation not found",
|
|
544
|
+
code: "INSTALLATION_NOT_FOUND",
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
// For local development, we only support install ID 1
|
|
549
|
+
if (installId !== 1) {
|
|
550
|
+
return res.status(404).json({
|
|
551
|
+
error: {
|
|
552
|
+
message: `Installation with ID ${installId} not found`,
|
|
553
|
+
code: "INSTALLATION_NOT_FOUND",
|
|
554
|
+
},
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
// Validate required fields
|
|
558
|
+
if (!form_page) {
|
|
559
|
+
return res.status(400).json({
|
|
560
|
+
error: {
|
|
561
|
+
message: "section is required",
|
|
562
|
+
code: "INVALID_REQUEST",
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
if (!action) {
|
|
567
|
+
return res.status(400).json({
|
|
568
|
+
error: {
|
|
569
|
+
message: "action is required",
|
|
570
|
+
code: "INVALID_REQUEST",
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
// Parse form_data_json if it's a string
|
|
576
|
+
let formData;
|
|
577
|
+
if (typeof form_data_json === "string") {
|
|
578
|
+
try {
|
|
579
|
+
formData = JSON.parse(form_data_json);
|
|
580
|
+
}
|
|
581
|
+
catch (e) {
|
|
582
|
+
formData = {};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
formData = form_data_json || {};
|
|
587
|
+
}
|
|
588
|
+
// Execute the onSettingsForm lifecycle method first
|
|
589
|
+
const lifecycleResult = await lifecycleExecutor.executeSettingsForm(form_page, action, formData);
|
|
590
|
+
if (lifecycleResult.success) {
|
|
591
|
+
// Get the lifecycle result which should be in the format: { result: LifecycleSettingsResponse, data: FormData }
|
|
592
|
+
const lifecycleData = lifecycleResult.result;
|
|
593
|
+
// Extract the response component from the lifecycle result
|
|
594
|
+
let settingsResponse = {};
|
|
595
|
+
if (lifecycleData && typeof lifecycleData === "object") {
|
|
596
|
+
settingsResponse = lifecycleData.result || {};
|
|
597
|
+
}
|
|
598
|
+
// Get the updated form data from settings store (always return fresh data)
|
|
599
|
+
const sections = settingsStore.getSections();
|
|
600
|
+
const updatedFormData = {};
|
|
601
|
+
sections.forEach((sectionName) => {
|
|
602
|
+
const sectionSettings = settingsStore.getSectionSettings(sectionName);
|
|
603
|
+
updatedFormData[sectionName] = sectionSettings;
|
|
604
|
+
});
|
|
605
|
+
// Return response in the format expected by the REST API (matching AnduinServer.submitForm)
|
|
606
|
+
res.json({
|
|
607
|
+
form_data_json: JSON.stringify(updatedFormData),
|
|
608
|
+
response_json: JSON.stringify(settingsResponse),
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
// Lifecycle execution failed
|
|
613
|
+
res.status(500).json({
|
|
614
|
+
error: {
|
|
615
|
+
message: "Settings form processing failed during lifecycle execution",
|
|
616
|
+
code: "LIFECYCLE_EXECUTION_FAILED",
|
|
617
|
+
details: {
|
|
618
|
+
logs: lifecycleResult.logs,
|
|
619
|
+
error: lifecycleResult.error,
|
|
620
|
+
executionTime: lifecycleResult.executionTime,
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
catch (error) {
|
|
627
|
+
// Unexpected error during settings form processing
|
|
628
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
629
|
+
res.status(500).json({
|
|
630
|
+
error: {
|
|
631
|
+
message: `Settings form processing failed: ${errorMessage}`,
|
|
632
|
+
code: "SETTINGS_FORM_ERROR",
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
/**
|
|
638
|
+
* GET /v1/installs/{app_install_id}/canUninstall
|
|
639
|
+
* Checks if an app installation can be uninstalled
|
|
640
|
+
*/
|
|
641
|
+
router.get("/:app_install_id/canUninstall", async (req, res) => {
|
|
642
|
+
const { app_install_id } = req.params;
|
|
643
|
+
// Validate app_install_id (should be numeric for our implementation)
|
|
644
|
+
const installId = parseInt(app_install_id, 10);
|
|
645
|
+
if (isNaN(installId)) {
|
|
646
|
+
return res.status(400).json({
|
|
647
|
+
error: {
|
|
648
|
+
message: "app_install_id must be a valid number",
|
|
649
|
+
code: "INVALID_INSTALL_ID",
|
|
650
|
+
},
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
// Check if app is installed (for local dev, we use install ID 1)
|
|
654
|
+
if (!configStore.isInstalled()) {
|
|
655
|
+
return res.status(404).json({
|
|
656
|
+
error: {
|
|
657
|
+
message: "App installation not found",
|
|
658
|
+
code: "INSTALLATION_NOT_FOUND",
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
// For local development, we only support install ID 1
|
|
663
|
+
if (installId !== 1) {
|
|
664
|
+
return res.status(404).json({
|
|
665
|
+
error: {
|
|
666
|
+
message: `Installation with ID ${installId} not found`,
|
|
667
|
+
code: "INSTALLATION_NOT_FOUND",
|
|
668
|
+
},
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
try {
|
|
672
|
+
// Execute the canUninstall lifecycle method
|
|
673
|
+
const lifecycleResult = await lifecycleExecutor.executeCanUninstall();
|
|
674
|
+
if (lifecycleResult.success) {
|
|
675
|
+
// Parse the result from the lifecycle method
|
|
676
|
+
const canUninstallResult = lifecycleResult.result;
|
|
677
|
+
// Return the response in the expected format
|
|
678
|
+
res.json({
|
|
679
|
+
uninstallable: canUninstallResult?.uninstallable || false,
|
|
680
|
+
message: canUninstallResult?.message,
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
// Lifecycle execution failed - return error response
|
|
685
|
+
res.status(500).json({
|
|
686
|
+
error: {
|
|
687
|
+
message: "Failed to check uninstall status during lifecycle execution",
|
|
688
|
+
code: "LIFECYCLE_EXECUTION_FAILED",
|
|
689
|
+
details: {
|
|
690
|
+
logs: lifecycleResult.logs,
|
|
691
|
+
error: lifecycleResult.error,
|
|
692
|
+
executionTime: lifecycleResult.executionTime,
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
// Unexpected error during canUninstall check - return error response
|
|
700
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
701
|
+
res.status(500).json({
|
|
702
|
+
error: {
|
|
703
|
+
message: `Error checking uninstall status: ${errorMessage}`,
|
|
704
|
+
code: "CAN_UNINSTALL_ERROR",
|
|
705
|
+
},
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
return router;
|
|
710
|
+
}
|
|
711
|
+
//# sourceMappingURL=v1.js.map
|