@orange-soft/strapi-deployment-trigger 1.0.1 → 1.1.2
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/admin/src/pages/HomePage.jsx +117 -62
- package/admin/src/pages/SettingsPage.jsx +274 -101
- package/dist/_chunks/App-CCbQMMHR.js +669 -0
- package/dist/_chunks/App-DRqMK_8x.mjs +669 -0
- package/dist/_chunks/{index-CqpMwL_C.js → index-SuWmJtOE.js} +1 -1
- package/dist/_chunks/{index-C18aSW5z.mjs → index-vQ0KWcpU.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +164 -24
- package/dist/server/index.mjs +164 -24
- package/package.json +1 -1
- package/server/src/controllers/controller.js +58 -3
- package/server/src/routes/admin/index.js +25 -0
- package/server/src/services/service.js +122 -24
- package/dist/_chunks/App-3JntxPYv.js +0 -520
- package/dist/_chunks/App-C0Byi5W1.mjs +0 -520
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -38,10 +38,36 @@ const controller = ({ strapi }) => ({
|
|
|
38
38
|
hasToken,
|
|
39
39
|
settings,
|
|
40
40
|
parsed: { owner, repo }
|
|
41
|
-
// Include parsed values for display
|
|
42
41
|
}
|
|
43
42
|
};
|
|
44
43
|
},
|
|
44
|
+
// Target CRUD operations
|
|
45
|
+
async addTarget(ctx) {
|
|
46
|
+
const { data } = ctx.request.body;
|
|
47
|
+
const service2 = getService(strapi);
|
|
48
|
+
const target = await service2.addTarget(data);
|
|
49
|
+
ctx.body = { data: target };
|
|
50
|
+
},
|
|
51
|
+
async updateTarget(ctx) {
|
|
52
|
+
const { id } = ctx.params;
|
|
53
|
+
const { data } = ctx.request.body;
|
|
54
|
+
const service2 = getService(strapi);
|
|
55
|
+
const target = await service2.updateTarget(id, data);
|
|
56
|
+
if (!target) {
|
|
57
|
+
return ctx.notFound("Target not found");
|
|
58
|
+
}
|
|
59
|
+
ctx.body = { data: target };
|
|
60
|
+
},
|
|
61
|
+
async deleteTarget(ctx) {
|
|
62
|
+
const { id } = ctx.params;
|
|
63
|
+
const service2 = getService(strapi);
|
|
64
|
+
const deleted = await service2.deleteTarget(id);
|
|
65
|
+
if (!deleted) {
|
|
66
|
+
return ctx.notFound("Target not found");
|
|
67
|
+
}
|
|
68
|
+
ctx.body = { data: { success: true } };
|
|
69
|
+
},
|
|
70
|
+
// Trigger deployment for a specific target
|
|
45
71
|
async trigger(ctx) {
|
|
46
72
|
const service2 = getService(strapi);
|
|
47
73
|
const githubToken = await service2.getToken();
|
|
@@ -53,7 +79,22 @@ const controller = ({ strapi }) => ({
|
|
|
53
79
|
if (!owner || !repo) {
|
|
54
80
|
return ctx.badRequest("GitHub repository URL is not configured or invalid. Please configure it in Settings.");
|
|
55
81
|
}
|
|
56
|
-
const { workflow
|
|
82
|
+
const { targetId, workflow: overrideWorkflow, branch: overrideBranch } = ctx.request.body || {};
|
|
83
|
+
let workflow, branch, targetName;
|
|
84
|
+
if (targetId) {
|
|
85
|
+
const target = await service2.getTarget(targetId);
|
|
86
|
+
if (!target) {
|
|
87
|
+
return ctx.badRequest("Target not found");
|
|
88
|
+
}
|
|
89
|
+
workflow = overrideWorkflow || target.workflow;
|
|
90
|
+
branch = overrideBranch || target.branch;
|
|
91
|
+
targetName = target.name;
|
|
92
|
+
} else {
|
|
93
|
+
const firstTarget = settings.targets?.[0];
|
|
94
|
+
workflow = overrideWorkflow || firstTarget?.workflow || "deploy.yml";
|
|
95
|
+
branch = overrideBranch || firstTarget?.branch || "master";
|
|
96
|
+
targetName = firstTarget?.name || "Default";
|
|
97
|
+
}
|
|
57
98
|
const url = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/dispatches`;
|
|
58
99
|
try {
|
|
59
100
|
const response = await fetch(url, {
|
|
@@ -70,13 +111,14 @@ const controller = ({ strapi }) => ({
|
|
|
70
111
|
strapi.log.error(`GitHub API error: ${response.status} - ${errorText}`);
|
|
71
112
|
return ctx.badRequest(`GitHub API error: ${response.status} - ${errorText}`);
|
|
72
113
|
}
|
|
73
|
-
strapi.log.info(`Deployment triggered for ${owner}/${repo} on branch ${branch}`);
|
|
114
|
+
strapi.log.info(`Deployment triggered for ${owner}/${repo} [${targetName}] on branch ${branch}`);
|
|
74
115
|
const actionsUrl = `https://github.com/${owner}/${repo}/actions`;
|
|
75
116
|
ctx.body = {
|
|
76
117
|
data: {
|
|
77
118
|
success: true,
|
|
78
119
|
message: `Deployment triggered successfully for ${owner}/${repo}`,
|
|
79
120
|
repository: `${owner}/${repo}`,
|
|
121
|
+
targetName,
|
|
80
122
|
workflow,
|
|
81
123
|
branch,
|
|
82
124
|
actionsUrl
|
|
@@ -141,6 +183,31 @@ const adminAPIRoutes = () => ({
|
|
|
141
183
|
config: {
|
|
142
184
|
policies: []
|
|
143
185
|
}
|
|
186
|
+
},
|
|
187
|
+
// Target CRUD routes
|
|
188
|
+
{
|
|
189
|
+
method: "POST",
|
|
190
|
+
path: "/targets",
|
|
191
|
+
handler: "controller.addTarget",
|
|
192
|
+
config: {
|
|
193
|
+
policies: []
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
method: "PUT",
|
|
198
|
+
path: "/targets/:id",
|
|
199
|
+
handler: "controller.updateTarget",
|
|
200
|
+
config: {
|
|
201
|
+
policies: []
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
method: "DELETE",
|
|
206
|
+
path: "/targets/:id",
|
|
207
|
+
handler: "controller.deleteTarget",
|
|
208
|
+
config: {
|
|
209
|
+
policies: []
|
|
210
|
+
}
|
|
144
211
|
}
|
|
145
212
|
]
|
|
146
213
|
});
|
|
@@ -152,9 +219,10 @@ const PLUGIN_ID = "deployment-trigger";
|
|
|
152
219
|
const STORE_KEY = "settings";
|
|
153
220
|
const DEFAULT_SETTINGS = {
|
|
154
221
|
repoUrl: "",
|
|
155
|
-
|
|
156
|
-
|
|
222
|
+
githubToken: "",
|
|
223
|
+
targets: []
|
|
157
224
|
};
|
|
225
|
+
const generateId = () => Math.random().toString(36).substring(2, 9);
|
|
158
226
|
const service = ({ strapi }) => ({
|
|
159
227
|
getWelcomeMessage() {
|
|
160
228
|
return "Welcome to Strapi Deployment Trigger";
|
|
@@ -177,24 +245,47 @@ const service = ({ strapi }) => ({
|
|
|
177
245
|
}
|
|
178
246
|
return { owner: null, repo: null };
|
|
179
247
|
},
|
|
248
|
+
// Migrate old single-config format to new multi-target format
|
|
249
|
+
migrateSettings(settings) {
|
|
250
|
+
if (!settings) return null;
|
|
251
|
+
if (Array.isArray(settings.targets)) {
|
|
252
|
+
return settings;
|
|
253
|
+
}
|
|
254
|
+
if (settings.workflow || settings.branch) {
|
|
255
|
+
return {
|
|
256
|
+
repoUrl: settings.repoUrl || "",
|
|
257
|
+
githubToken: settings.githubToken || "",
|
|
258
|
+
targets: [{
|
|
259
|
+
id: generateId(),
|
|
260
|
+
name: "Default",
|
|
261
|
+
workflow: settings.workflow || "deploy.yml",
|
|
262
|
+
branch: settings.branch || "master"
|
|
263
|
+
}]
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return settings;
|
|
267
|
+
},
|
|
180
268
|
async getSettings() {
|
|
181
269
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
270
|
+
let dbSettings = await store.get({ key: STORE_KEY });
|
|
271
|
+
const needsMigration = dbSettings && !Array.isArray(dbSettings.targets) && (dbSettings.workflow || dbSettings.branch);
|
|
272
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
273
|
+
if (needsMigration && dbSettings) {
|
|
274
|
+
await store.set({ key: STORE_KEY, value: dbSettings });
|
|
275
|
+
}
|
|
276
|
+
if (!dbSettings) {
|
|
185
277
|
return {
|
|
186
|
-
...
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
hasToken: !!token2,
|
|
190
|
-
maskedToken: this.maskToken(token2)
|
|
278
|
+
...DEFAULT_SETTINGS,
|
|
279
|
+
hasToken: false,
|
|
280
|
+
maskedToken: null
|
|
191
281
|
};
|
|
192
282
|
}
|
|
193
|
-
const token = dbSettings
|
|
283
|
+
const token = dbSettings.githubToken;
|
|
194
284
|
return {
|
|
195
|
-
|
|
196
|
-
|
|
285
|
+
repoUrl: dbSettings.repoUrl || "",
|
|
286
|
+
targets: dbSettings.targets || [],
|
|
197
287
|
githubToken: void 0,
|
|
288
|
+
// Never expose full token
|
|
198
289
|
hasToken: !!token,
|
|
199
290
|
maskedToken: this.maskToken(token)
|
|
200
291
|
};
|
|
@@ -202,30 +293,79 @@ const service = ({ strapi }) => ({
|
|
|
202
293
|
async saveSettings(settings) {
|
|
203
294
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
204
295
|
const existingSettings = await store.get({ key: STORE_KEY }) || {};
|
|
296
|
+
const migrated = this.migrateSettings(existingSettings);
|
|
205
297
|
const settingsToSave = {
|
|
206
|
-
repoUrl: settings.repoUrl,
|
|
207
|
-
|
|
208
|
-
branch: settings.branch || DEFAULT_SETTINGS.branch,
|
|
298
|
+
repoUrl: settings.repoUrl !== void 0 ? settings.repoUrl : migrated?.repoUrl || "",
|
|
299
|
+
targets: settings.targets !== void 0 ? settings.targets : migrated?.targets || [],
|
|
209
300
|
// Only update token if a new one is provided
|
|
210
|
-
githubToken: settings.githubToken ||
|
|
301
|
+
githubToken: settings.githubToken || migrated?.githubToken || ""
|
|
211
302
|
};
|
|
212
303
|
await store.set({ key: STORE_KEY, value: settingsToSave });
|
|
213
304
|
return {
|
|
214
|
-
|
|
305
|
+
repoUrl: settingsToSave.repoUrl,
|
|
306
|
+
targets: settingsToSave.targets,
|
|
215
307
|
githubToken: void 0,
|
|
216
|
-
hasToken: !!settingsToSave.githubToken
|
|
308
|
+
hasToken: !!settingsToSave.githubToken,
|
|
309
|
+
maskedToken: this.maskToken(settingsToSave.githubToken)
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
async addTarget(target) {
|
|
313
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
314
|
+
let settings = await store.get({ key: STORE_KEY }) || DEFAULT_SETTINGS;
|
|
315
|
+
settings = this.migrateSettings(settings) || DEFAULT_SETTINGS;
|
|
316
|
+
const newTarget = {
|
|
317
|
+
id: generateId(),
|
|
318
|
+
name: target.name || "New Target",
|
|
319
|
+
workflow: target.workflow || "deploy.yml",
|
|
320
|
+
branch: target.branch || "master"
|
|
217
321
|
};
|
|
322
|
+
settings.targets = [...settings.targets || [], newTarget];
|
|
323
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
324
|
+
return newTarget;
|
|
325
|
+
},
|
|
326
|
+
async updateTarget(targetId, updates) {
|
|
327
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
328
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
329
|
+
settings = this.migrateSettings(settings);
|
|
330
|
+
if (!settings?.targets) return null;
|
|
331
|
+
const targetIndex = settings.targets.findIndex((t) => t.id === targetId);
|
|
332
|
+
if (targetIndex === -1) return null;
|
|
333
|
+
settings.targets[targetIndex] = {
|
|
334
|
+
...settings.targets[targetIndex],
|
|
335
|
+
...updates,
|
|
336
|
+
id: targetId
|
|
337
|
+
// Preserve ID
|
|
338
|
+
};
|
|
339
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
340
|
+
return settings.targets[targetIndex];
|
|
341
|
+
},
|
|
342
|
+
async deleteTarget(targetId) {
|
|
343
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
344
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
345
|
+
settings = this.migrateSettings(settings);
|
|
346
|
+
if (!settings?.targets) return false;
|
|
347
|
+
const initialLength = settings.targets.length;
|
|
348
|
+
settings.targets = settings.targets.filter((t) => t.id !== targetId);
|
|
349
|
+
if (settings.targets.length === initialLength) return false;
|
|
350
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
351
|
+
return true;
|
|
352
|
+
},
|
|
353
|
+
async getTarget(targetId) {
|
|
354
|
+
const settings = await this.getSettings();
|
|
355
|
+
return settings.targets?.find((t) => t.id === targetId) || null;
|
|
218
356
|
},
|
|
219
357
|
async getToken() {
|
|
220
358
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
221
|
-
|
|
359
|
+
let dbSettings = await store.get({ key: STORE_KEY });
|
|
360
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
222
361
|
return dbSettings?.githubToken || null;
|
|
223
362
|
},
|
|
224
363
|
async isConfigured() {
|
|
225
364
|
const settings = await this.getSettings();
|
|
226
365
|
const hasToken = await this.getToken();
|
|
227
366
|
const { owner, repo } = this.parseRepoUrl(settings.repoUrl);
|
|
228
|
-
|
|
367
|
+
const hasTargets = settings.targets && settings.targets.length > 0;
|
|
368
|
+
return !!(owner && repo && hasToken && hasTargets);
|
|
229
369
|
},
|
|
230
370
|
async hasToken() {
|
|
231
371
|
const token = await this.getToken();
|
package/dist/server/index.mjs
CHANGED
|
@@ -37,10 +37,36 @@ const controller = ({ strapi }) => ({
|
|
|
37
37
|
hasToken,
|
|
38
38
|
settings,
|
|
39
39
|
parsed: { owner, repo }
|
|
40
|
-
// Include parsed values for display
|
|
41
40
|
}
|
|
42
41
|
};
|
|
43
42
|
},
|
|
43
|
+
// Target CRUD operations
|
|
44
|
+
async addTarget(ctx) {
|
|
45
|
+
const { data } = ctx.request.body;
|
|
46
|
+
const service2 = getService(strapi);
|
|
47
|
+
const target = await service2.addTarget(data);
|
|
48
|
+
ctx.body = { data: target };
|
|
49
|
+
},
|
|
50
|
+
async updateTarget(ctx) {
|
|
51
|
+
const { id } = ctx.params;
|
|
52
|
+
const { data } = ctx.request.body;
|
|
53
|
+
const service2 = getService(strapi);
|
|
54
|
+
const target = await service2.updateTarget(id, data);
|
|
55
|
+
if (!target) {
|
|
56
|
+
return ctx.notFound("Target not found");
|
|
57
|
+
}
|
|
58
|
+
ctx.body = { data: target };
|
|
59
|
+
},
|
|
60
|
+
async deleteTarget(ctx) {
|
|
61
|
+
const { id } = ctx.params;
|
|
62
|
+
const service2 = getService(strapi);
|
|
63
|
+
const deleted = await service2.deleteTarget(id);
|
|
64
|
+
if (!deleted) {
|
|
65
|
+
return ctx.notFound("Target not found");
|
|
66
|
+
}
|
|
67
|
+
ctx.body = { data: { success: true } };
|
|
68
|
+
},
|
|
69
|
+
// Trigger deployment for a specific target
|
|
44
70
|
async trigger(ctx) {
|
|
45
71
|
const service2 = getService(strapi);
|
|
46
72
|
const githubToken = await service2.getToken();
|
|
@@ -52,7 +78,22 @@ const controller = ({ strapi }) => ({
|
|
|
52
78
|
if (!owner || !repo) {
|
|
53
79
|
return ctx.badRequest("GitHub repository URL is not configured or invalid. Please configure it in Settings.");
|
|
54
80
|
}
|
|
55
|
-
const { workflow
|
|
81
|
+
const { targetId, workflow: overrideWorkflow, branch: overrideBranch } = ctx.request.body || {};
|
|
82
|
+
let workflow, branch, targetName;
|
|
83
|
+
if (targetId) {
|
|
84
|
+
const target = await service2.getTarget(targetId);
|
|
85
|
+
if (!target) {
|
|
86
|
+
return ctx.badRequest("Target not found");
|
|
87
|
+
}
|
|
88
|
+
workflow = overrideWorkflow || target.workflow;
|
|
89
|
+
branch = overrideBranch || target.branch;
|
|
90
|
+
targetName = target.name;
|
|
91
|
+
} else {
|
|
92
|
+
const firstTarget = settings.targets?.[0];
|
|
93
|
+
workflow = overrideWorkflow || firstTarget?.workflow || "deploy.yml";
|
|
94
|
+
branch = overrideBranch || firstTarget?.branch || "master";
|
|
95
|
+
targetName = firstTarget?.name || "Default";
|
|
96
|
+
}
|
|
56
97
|
const url = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/dispatches`;
|
|
57
98
|
try {
|
|
58
99
|
const response = await fetch(url, {
|
|
@@ -69,13 +110,14 @@ const controller = ({ strapi }) => ({
|
|
|
69
110
|
strapi.log.error(`GitHub API error: ${response.status} - ${errorText}`);
|
|
70
111
|
return ctx.badRequest(`GitHub API error: ${response.status} - ${errorText}`);
|
|
71
112
|
}
|
|
72
|
-
strapi.log.info(`Deployment triggered for ${owner}/${repo} on branch ${branch}`);
|
|
113
|
+
strapi.log.info(`Deployment triggered for ${owner}/${repo} [${targetName}] on branch ${branch}`);
|
|
73
114
|
const actionsUrl = `https://github.com/${owner}/${repo}/actions`;
|
|
74
115
|
ctx.body = {
|
|
75
116
|
data: {
|
|
76
117
|
success: true,
|
|
77
118
|
message: `Deployment triggered successfully for ${owner}/${repo}`,
|
|
78
119
|
repository: `${owner}/${repo}`,
|
|
120
|
+
targetName,
|
|
79
121
|
workflow,
|
|
80
122
|
branch,
|
|
81
123
|
actionsUrl
|
|
@@ -140,6 +182,31 @@ const adminAPIRoutes = () => ({
|
|
|
140
182
|
config: {
|
|
141
183
|
policies: []
|
|
142
184
|
}
|
|
185
|
+
},
|
|
186
|
+
// Target CRUD routes
|
|
187
|
+
{
|
|
188
|
+
method: "POST",
|
|
189
|
+
path: "/targets",
|
|
190
|
+
handler: "controller.addTarget",
|
|
191
|
+
config: {
|
|
192
|
+
policies: []
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
method: "PUT",
|
|
197
|
+
path: "/targets/:id",
|
|
198
|
+
handler: "controller.updateTarget",
|
|
199
|
+
config: {
|
|
200
|
+
policies: []
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
method: "DELETE",
|
|
205
|
+
path: "/targets/:id",
|
|
206
|
+
handler: "controller.deleteTarget",
|
|
207
|
+
config: {
|
|
208
|
+
policies: []
|
|
209
|
+
}
|
|
143
210
|
}
|
|
144
211
|
]
|
|
145
212
|
});
|
|
@@ -151,9 +218,10 @@ const PLUGIN_ID = "deployment-trigger";
|
|
|
151
218
|
const STORE_KEY = "settings";
|
|
152
219
|
const DEFAULT_SETTINGS = {
|
|
153
220
|
repoUrl: "",
|
|
154
|
-
|
|
155
|
-
|
|
221
|
+
githubToken: "",
|
|
222
|
+
targets: []
|
|
156
223
|
};
|
|
224
|
+
const generateId = () => Math.random().toString(36).substring(2, 9);
|
|
157
225
|
const service = ({ strapi }) => ({
|
|
158
226
|
getWelcomeMessage() {
|
|
159
227
|
return "Welcome to Strapi Deployment Trigger";
|
|
@@ -176,24 +244,47 @@ const service = ({ strapi }) => ({
|
|
|
176
244
|
}
|
|
177
245
|
return { owner: null, repo: null };
|
|
178
246
|
},
|
|
247
|
+
// Migrate old single-config format to new multi-target format
|
|
248
|
+
migrateSettings(settings) {
|
|
249
|
+
if (!settings) return null;
|
|
250
|
+
if (Array.isArray(settings.targets)) {
|
|
251
|
+
return settings;
|
|
252
|
+
}
|
|
253
|
+
if (settings.workflow || settings.branch) {
|
|
254
|
+
return {
|
|
255
|
+
repoUrl: settings.repoUrl || "",
|
|
256
|
+
githubToken: settings.githubToken || "",
|
|
257
|
+
targets: [{
|
|
258
|
+
id: generateId(),
|
|
259
|
+
name: "Default",
|
|
260
|
+
workflow: settings.workflow || "deploy.yml",
|
|
261
|
+
branch: settings.branch || "master"
|
|
262
|
+
}]
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return settings;
|
|
266
|
+
},
|
|
179
267
|
async getSettings() {
|
|
180
268
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
269
|
+
let dbSettings = await store.get({ key: STORE_KEY });
|
|
270
|
+
const needsMigration = dbSettings && !Array.isArray(dbSettings.targets) && (dbSettings.workflow || dbSettings.branch);
|
|
271
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
272
|
+
if (needsMigration && dbSettings) {
|
|
273
|
+
await store.set({ key: STORE_KEY, value: dbSettings });
|
|
274
|
+
}
|
|
275
|
+
if (!dbSettings) {
|
|
184
276
|
return {
|
|
185
|
-
...
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
hasToken: !!token2,
|
|
189
|
-
maskedToken: this.maskToken(token2)
|
|
277
|
+
...DEFAULT_SETTINGS,
|
|
278
|
+
hasToken: false,
|
|
279
|
+
maskedToken: null
|
|
190
280
|
};
|
|
191
281
|
}
|
|
192
|
-
const token = dbSettings
|
|
282
|
+
const token = dbSettings.githubToken;
|
|
193
283
|
return {
|
|
194
|
-
|
|
195
|
-
|
|
284
|
+
repoUrl: dbSettings.repoUrl || "",
|
|
285
|
+
targets: dbSettings.targets || [],
|
|
196
286
|
githubToken: void 0,
|
|
287
|
+
// Never expose full token
|
|
197
288
|
hasToken: !!token,
|
|
198
289
|
maskedToken: this.maskToken(token)
|
|
199
290
|
};
|
|
@@ -201,30 +292,79 @@ const service = ({ strapi }) => ({
|
|
|
201
292
|
async saveSettings(settings) {
|
|
202
293
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
203
294
|
const existingSettings = await store.get({ key: STORE_KEY }) || {};
|
|
295
|
+
const migrated = this.migrateSettings(existingSettings);
|
|
204
296
|
const settingsToSave = {
|
|
205
|
-
repoUrl: settings.repoUrl,
|
|
206
|
-
|
|
207
|
-
branch: settings.branch || DEFAULT_SETTINGS.branch,
|
|
297
|
+
repoUrl: settings.repoUrl !== void 0 ? settings.repoUrl : migrated?.repoUrl || "",
|
|
298
|
+
targets: settings.targets !== void 0 ? settings.targets : migrated?.targets || [],
|
|
208
299
|
// Only update token if a new one is provided
|
|
209
|
-
githubToken: settings.githubToken ||
|
|
300
|
+
githubToken: settings.githubToken || migrated?.githubToken || ""
|
|
210
301
|
};
|
|
211
302
|
await store.set({ key: STORE_KEY, value: settingsToSave });
|
|
212
303
|
return {
|
|
213
|
-
|
|
304
|
+
repoUrl: settingsToSave.repoUrl,
|
|
305
|
+
targets: settingsToSave.targets,
|
|
214
306
|
githubToken: void 0,
|
|
215
|
-
hasToken: !!settingsToSave.githubToken
|
|
307
|
+
hasToken: !!settingsToSave.githubToken,
|
|
308
|
+
maskedToken: this.maskToken(settingsToSave.githubToken)
|
|
309
|
+
};
|
|
310
|
+
},
|
|
311
|
+
async addTarget(target) {
|
|
312
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
313
|
+
let settings = await store.get({ key: STORE_KEY }) || DEFAULT_SETTINGS;
|
|
314
|
+
settings = this.migrateSettings(settings) || DEFAULT_SETTINGS;
|
|
315
|
+
const newTarget = {
|
|
316
|
+
id: generateId(),
|
|
317
|
+
name: target.name || "New Target",
|
|
318
|
+
workflow: target.workflow || "deploy.yml",
|
|
319
|
+
branch: target.branch || "master"
|
|
216
320
|
};
|
|
321
|
+
settings.targets = [...settings.targets || [], newTarget];
|
|
322
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
323
|
+
return newTarget;
|
|
324
|
+
},
|
|
325
|
+
async updateTarget(targetId, updates) {
|
|
326
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
327
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
328
|
+
settings = this.migrateSettings(settings);
|
|
329
|
+
if (!settings?.targets) return null;
|
|
330
|
+
const targetIndex = settings.targets.findIndex((t) => t.id === targetId);
|
|
331
|
+
if (targetIndex === -1) return null;
|
|
332
|
+
settings.targets[targetIndex] = {
|
|
333
|
+
...settings.targets[targetIndex],
|
|
334
|
+
...updates,
|
|
335
|
+
id: targetId
|
|
336
|
+
// Preserve ID
|
|
337
|
+
};
|
|
338
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
339
|
+
return settings.targets[targetIndex];
|
|
340
|
+
},
|
|
341
|
+
async deleteTarget(targetId) {
|
|
342
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
343
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
344
|
+
settings = this.migrateSettings(settings);
|
|
345
|
+
if (!settings?.targets) return false;
|
|
346
|
+
const initialLength = settings.targets.length;
|
|
347
|
+
settings.targets = settings.targets.filter((t) => t.id !== targetId);
|
|
348
|
+
if (settings.targets.length === initialLength) return false;
|
|
349
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
350
|
+
return true;
|
|
351
|
+
},
|
|
352
|
+
async getTarget(targetId) {
|
|
353
|
+
const settings = await this.getSettings();
|
|
354
|
+
return settings.targets?.find((t) => t.id === targetId) || null;
|
|
217
355
|
},
|
|
218
356
|
async getToken() {
|
|
219
357
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
220
|
-
|
|
358
|
+
let dbSettings = await store.get({ key: STORE_KEY });
|
|
359
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
221
360
|
return dbSettings?.githubToken || null;
|
|
222
361
|
},
|
|
223
362
|
async isConfigured() {
|
|
224
363
|
const settings = await this.getSettings();
|
|
225
364
|
const hasToken = await this.getToken();
|
|
226
365
|
const { owner, repo } = this.parseRepoUrl(settings.repoUrl);
|
|
227
|
-
|
|
366
|
+
const hasTargets = settings.targets && settings.targets.length > 0;
|
|
367
|
+
return !!(owner && repo && hasToken && hasTargets);
|
|
228
368
|
},
|
|
229
369
|
async hasToken() {
|
|
230
370
|
const token = await this.getToken();
|
package/package.json
CHANGED
|
@@ -30,11 +30,45 @@ const controller = ({ strapi }) => ({
|
|
|
30
30
|
configured,
|
|
31
31
|
hasToken,
|
|
32
32
|
settings,
|
|
33
|
-
parsed: { owner, repo },
|
|
33
|
+
parsed: { owner, repo },
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
},
|
|
37
37
|
|
|
38
|
+
// Target CRUD operations
|
|
39
|
+
async addTarget(ctx) {
|
|
40
|
+
const { data } = ctx.request.body;
|
|
41
|
+
const service = getService(strapi);
|
|
42
|
+
const target = await service.addTarget(data);
|
|
43
|
+
ctx.body = { data: target };
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async updateTarget(ctx) {
|
|
47
|
+
const { id } = ctx.params;
|
|
48
|
+
const { data } = ctx.request.body;
|
|
49
|
+
const service = getService(strapi);
|
|
50
|
+
const target = await service.updateTarget(id, data);
|
|
51
|
+
|
|
52
|
+
if (!target) {
|
|
53
|
+
return ctx.notFound('Target not found');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
ctx.body = { data: target };
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
async deleteTarget(ctx) {
|
|
60
|
+
const { id } = ctx.params;
|
|
61
|
+
const service = getService(strapi);
|
|
62
|
+
const deleted = await service.deleteTarget(id);
|
|
63
|
+
|
|
64
|
+
if (!deleted) {
|
|
65
|
+
return ctx.notFound('Target not found');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ctx.body = { data: { success: true } };
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Trigger deployment for a specific target
|
|
38
72
|
async trigger(ctx) {
|
|
39
73
|
const service = getService(strapi);
|
|
40
74
|
const githubToken = await service.getToken();
|
|
@@ -50,7 +84,27 @@ const controller = ({ strapi }) => ({
|
|
|
50
84
|
return ctx.badRequest('GitHub repository URL is not configured or invalid. Please configure it in Settings.');
|
|
51
85
|
}
|
|
52
86
|
|
|
53
|
-
|
|
87
|
+
// Get target from request body or params
|
|
88
|
+
const { targetId, workflow: overrideWorkflow, branch: overrideBranch } = ctx.request.body || {};
|
|
89
|
+
|
|
90
|
+
let workflow, branch, targetName;
|
|
91
|
+
|
|
92
|
+
if (targetId) {
|
|
93
|
+
// Trigger specific target
|
|
94
|
+
const target = await service.getTarget(targetId);
|
|
95
|
+
if (!target) {
|
|
96
|
+
return ctx.badRequest('Target not found');
|
|
97
|
+
}
|
|
98
|
+
workflow = overrideWorkflow || target.workflow;
|
|
99
|
+
branch = overrideBranch || target.branch;
|
|
100
|
+
targetName = target.name;
|
|
101
|
+
} else {
|
|
102
|
+
// Fallback for backwards compatibility - use first target or provided values
|
|
103
|
+
const firstTarget = settings.targets?.[0];
|
|
104
|
+
workflow = overrideWorkflow || firstTarget?.workflow || 'deploy.yml';
|
|
105
|
+
branch = overrideBranch || firstTarget?.branch || 'master';
|
|
106
|
+
targetName = firstTarget?.name || 'Default';
|
|
107
|
+
}
|
|
54
108
|
|
|
55
109
|
const url = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/dispatches`;
|
|
56
110
|
|
|
@@ -71,7 +125,7 @@ const controller = ({ strapi }) => ({
|
|
|
71
125
|
return ctx.badRequest(`GitHub API error: ${response.status} - ${errorText}`);
|
|
72
126
|
}
|
|
73
127
|
|
|
74
|
-
strapi.log.info(`Deployment triggered for ${owner}/${repo} on branch ${branch}`);
|
|
128
|
+
strapi.log.info(`Deployment triggered for ${owner}/${repo} [${targetName}] on branch ${branch}`);
|
|
75
129
|
|
|
76
130
|
const actionsUrl = `https://github.com/${owner}/${repo}/actions`;
|
|
77
131
|
|
|
@@ -80,6 +134,7 @@ const controller = ({ strapi }) => ({
|
|
|
80
134
|
success: true,
|
|
81
135
|
message: `Deployment triggered successfully for ${owner}/${repo}`,
|
|
82
136
|
repository: `${owner}/${repo}`,
|
|
137
|
+
targetName,
|
|
83
138
|
workflow,
|
|
84
139
|
branch,
|
|
85
140
|
actionsUrl,
|
|
@@ -33,5 +33,30 @@ export default () => ({
|
|
|
33
33
|
policies: [],
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
|
+
// Target CRUD routes
|
|
37
|
+
{
|
|
38
|
+
method: 'POST',
|
|
39
|
+
path: '/targets',
|
|
40
|
+
handler: 'controller.addTarget',
|
|
41
|
+
config: {
|
|
42
|
+
policies: [],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
method: 'PUT',
|
|
47
|
+
path: '/targets/:id',
|
|
48
|
+
handler: 'controller.updateTarget',
|
|
49
|
+
config: {
|
|
50
|
+
policies: [],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
method: 'DELETE',
|
|
55
|
+
path: '/targets/:id',
|
|
56
|
+
handler: 'controller.deleteTarget',
|
|
57
|
+
config: {
|
|
58
|
+
policies: [],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
36
61
|
],
|
|
37
62
|
});
|