@orange-soft/strapi-deployment-trigger 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/pages/HomePage.jsx +109 -61
- package/admin/src/pages/SettingsPage.jsx +270 -97
- package/dist/_chunks/App-CuSCtdH7.js +666 -0
- package/dist/_chunks/App-vakyp6FE.mjs +666 -0
- package/dist/_chunks/{index-C18aSW5z.mjs → index-CZWWYGR3.mjs} +1 -1
- package/dist/_chunks/{index-CqpMwL_C.js → index-DGatQB-9.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +160 -24
- package/dist/server/index.mjs +160 -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 +114 -24
- package/dist/_chunks/App-3JntxPYv.js +0 -520
- package/dist/_chunks/App-C0Byi5W1.mjs +0 -520
|
@@ -37,7 +37,7 @@ const index = {
|
|
|
37
37
|
defaultMessage: PLUGIN_ID
|
|
38
38
|
},
|
|
39
39
|
Component: async () => {
|
|
40
|
-
const { App } = await Promise.resolve().then(() => require("./App-
|
|
40
|
+
const { App } = await Promise.resolve().then(() => require("./App-CuSCtdH7.js"));
|
|
41
41
|
return App;
|
|
42
42
|
}
|
|
43
43
|
});
|
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,43 @@ 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
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
272
|
+
if (!dbSettings) {
|
|
185
273
|
return {
|
|
186
|
-
...
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
hasToken: !!token2,
|
|
190
|
-
maskedToken: this.maskToken(token2)
|
|
274
|
+
...DEFAULT_SETTINGS,
|
|
275
|
+
hasToken: false,
|
|
276
|
+
maskedToken: null
|
|
191
277
|
};
|
|
192
278
|
}
|
|
193
|
-
const token = dbSettings
|
|
279
|
+
const token = dbSettings.githubToken;
|
|
194
280
|
return {
|
|
195
|
-
|
|
196
|
-
|
|
281
|
+
repoUrl: dbSettings.repoUrl || "",
|
|
282
|
+
targets: dbSettings.targets || [],
|
|
197
283
|
githubToken: void 0,
|
|
284
|
+
// Never expose full token
|
|
198
285
|
hasToken: !!token,
|
|
199
286
|
maskedToken: this.maskToken(token)
|
|
200
287
|
};
|
|
@@ -202,30 +289,79 @@ const service = ({ strapi }) => ({
|
|
|
202
289
|
async saveSettings(settings) {
|
|
203
290
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
204
291
|
const existingSettings = await store.get({ key: STORE_KEY }) || {};
|
|
292
|
+
const migrated = this.migrateSettings(existingSettings);
|
|
205
293
|
const settingsToSave = {
|
|
206
|
-
repoUrl: settings.repoUrl,
|
|
207
|
-
|
|
208
|
-
branch: settings.branch || DEFAULT_SETTINGS.branch,
|
|
294
|
+
repoUrl: settings.repoUrl !== void 0 ? settings.repoUrl : migrated?.repoUrl || "",
|
|
295
|
+
targets: settings.targets !== void 0 ? settings.targets : migrated?.targets || [],
|
|
209
296
|
// Only update token if a new one is provided
|
|
210
|
-
githubToken: settings.githubToken ||
|
|
297
|
+
githubToken: settings.githubToken || migrated?.githubToken || ""
|
|
211
298
|
};
|
|
212
299
|
await store.set({ key: STORE_KEY, value: settingsToSave });
|
|
213
300
|
return {
|
|
214
|
-
|
|
301
|
+
repoUrl: settingsToSave.repoUrl,
|
|
302
|
+
targets: settingsToSave.targets,
|
|
215
303
|
githubToken: void 0,
|
|
216
|
-
hasToken: !!settingsToSave.githubToken
|
|
304
|
+
hasToken: !!settingsToSave.githubToken,
|
|
305
|
+
maskedToken: this.maskToken(settingsToSave.githubToken)
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
async addTarget(target) {
|
|
309
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
310
|
+
let settings = await store.get({ key: STORE_KEY }) || DEFAULT_SETTINGS;
|
|
311
|
+
settings = this.migrateSettings(settings) || DEFAULT_SETTINGS;
|
|
312
|
+
const newTarget = {
|
|
313
|
+
id: generateId(),
|
|
314
|
+
name: target.name || "New Target",
|
|
315
|
+
workflow: target.workflow || "deploy.yml",
|
|
316
|
+
branch: target.branch || "master"
|
|
217
317
|
};
|
|
318
|
+
settings.targets = [...settings.targets || [], newTarget];
|
|
319
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
320
|
+
return newTarget;
|
|
321
|
+
},
|
|
322
|
+
async updateTarget(targetId, updates) {
|
|
323
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
324
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
325
|
+
settings = this.migrateSettings(settings);
|
|
326
|
+
if (!settings?.targets) return null;
|
|
327
|
+
const targetIndex = settings.targets.findIndex((t) => t.id === targetId);
|
|
328
|
+
if (targetIndex === -1) return null;
|
|
329
|
+
settings.targets[targetIndex] = {
|
|
330
|
+
...settings.targets[targetIndex],
|
|
331
|
+
...updates,
|
|
332
|
+
id: targetId
|
|
333
|
+
// Preserve ID
|
|
334
|
+
};
|
|
335
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
336
|
+
return settings.targets[targetIndex];
|
|
337
|
+
},
|
|
338
|
+
async deleteTarget(targetId) {
|
|
339
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
340
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
341
|
+
settings = this.migrateSettings(settings);
|
|
342
|
+
if (!settings?.targets) return false;
|
|
343
|
+
const initialLength = settings.targets.length;
|
|
344
|
+
settings.targets = settings.targets.filter((t) => t.id !== targetId);
|
|
345
|
+
if (settings.targets.length === initialLength) return false;
|
|
346
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
347
|
+
return true;
|
|
348
|
+
},
|
|
349
|
+
async getTarget(targetId) {
|
|
350
|
+
const settings = await this.getSettings();
|
|
351
|
+
return settings.targets?.find((t) => t.id === targetId) || null;
|
|
218
352
|
},
|
|
219
353
|
async getToken() {
|
|
220
354
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
221
|
-
|
|
355
|
+
let dbSettings = await store.get({ key: STORE_KEY });
|
|
356
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
222
357
|
return dbSettings?.githubToken || null;
|
|
223
358
|
},
|
|
224
359
|
async isConfigured() {
|
|
225
360
|
const settings = await this.getSettings();
|
|
226
361
|
const hasToken = await this.getToken();
|
|
227
362
|
const { owner, repo } = this.parseRepoUrl(settings.repoUrl);
|
|
228
|
-
|
|
363
|
+
const hasTargets = settings.targets && settings.targets.length > 0;
|
|
364
|
+
return !!(owner && repo && hasToken && hasTargets);
|
|
229
365
|
},
|
|
230
366
|
async hasToken() {
|
|
231
367
|
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,43 @@ 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
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
271
|
+
if (!dbSettings) {
|
|
184
272
|
return {
|
|
185
|
-
...
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
hasToken: !!token2,
|
|
189
|
-
maskedToken: this.maskToken(token2)
|
|
273
|
+
...DEFAULT_SETTINGS,
|
|
274
|
+
hasToken: false,
|
|
275
|
+
maskedToken: null
|
|
190
276
|
};
|
|
191
277
|
}
|
|
192
|
-
const token = dbSettings
|
|
278
|
+
const token = dbSettings.githubToken;
|
|
193
279
|
return {
|
|
194
|
-
|
|
195
|
-
|
|
280
|
+
repoUrl: dbSettings.repoUrl || "",
|
|
281
|
+
targets: dbSettings.targets || [],
|
|
196
282
|
githubToken: void 0,
|
|
283
|
+
// Never expose full token
|
|
197
284
|
hasToken: !!token,
|
|
198
285
|
maskedToken: this.maskToken(token)
|
|
199
286
|
};
|
|
@@ -201,30 +288,79 @@ const service = ({ strapi }) => ({
|
|
|
201
288
|
async saveSettings(settings) {
|
|
202
289
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
203
290
|
const existingSettings = await store.get({ key: STORE_KEY }) || {};
|
|
291
|
+
const migrated = this.migrateSettings(existingSettings);
|
|
204
292
|
const settingsToSave = {
|
|
205
|
-
repoUrl: settings.repoUrl,
|
|
206
|
-
|
|
207
|
-
branch: settings.branch || DEFAULT_SETTINGS.branch,
|
|
293
|
+
repoUrl: settings.repoUrl !== void 0 ? settings.repoUrl : migrated?.repoUrl || "",
|
|
294
|
+
targets: settings.targets !== void 0 ? settings.targets : migrated?.targets || [],
|
|
208
295
|
// Only update token if a new one is provided
|
|
209
|
-
githubToken: settings.githubToken ||
|
|
296
|
+
githubToken: settings.githubToken || migrated?.githubToken || ""
|
|
210
297
|
};
|
|
211
298
|
await store.set({ key: STORE_KEY, value: settingsToSave });
|
|
212
299
|
return {
|
|
213
|
-
|
|
300
|
+
repoUrl: settingsToSave.repoUrl,
|
|
301
|
+
targets: settingsToSave.targets,
|
|
214
302
|
githubToken: void 0,
|
|
215
|
-
hasToken: !!settingsToSave.githubToken
|
|
303
|
+
hasToken: !!settingsToSave.githubToken,
|
|
304
|
+
maskedToken: this.maskToken(settingsToSave.githubToken)
|
|
305
|
+
};
|
|
306
|
+
},
|
|
307
|
+
async addTarget(target) {
|
|
308
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
309
|
+
let settings = await store.get({ key: STORE_KEY }) || DEFAULT_SETTINGS;
|
|
310
|
+
settings = this.migrateSettings(settings) || DEFAULT_SETTINGS;
|
|
311
|
+
const newTarget = {
|
|
312
|
+
id: generateId(),
|
|
313
|
+
name: target.name || "New Target",
|
|
314
|
+
workflow: target.workflow || "deploy.yml",
|
|
315
|
+
branch: target.branch || "master"
|
|
216
316
|
};
|
|
317
|
+
settings.targets = [...settings.targets || [], newTarget];
|
|
318
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
319
|
+
return newTarget;
|
|
320
|
+
},
|
|
321
|
+
async updateTarget(targetId, updates) {
|
|
322
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
323
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
324
|
+
settings = this.migrateSettings(settings);
|
|
325
|
+
if (!settings?.targets) return null;
|
|
326
|
+
const targetIndex = settings.targets.findIndex((t) => t.id === targetId);
|
|
327
|
+
if (targetIndex === -1) return null;
|
|
328
|
+
settings.targets[targetIndex] = {
|
|
329
|
+
...settings.targets[targetIndex],
|
|
330
|
+
...updates,
|
|
331
|
+
id: targetId
|
|
332
|
+
// Preserve ID
|
|
333
|
+
};
|
|
334
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
335
|
+
return settings.targets[targetIndex];
|
|
336
|
+
},
|
|
337
|
+
async deleteTarget(targetId) {
|
|
338
|
+
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
339
|
+
let settings = await store.get({ key: STORE_KEY });
|
|
340
|
+
settings = this.migrateSettings(settings);
|
|
341
|
+
if (!settings?.targets) return false;
|
|
342
|
+
const initialLength = settings.targets.length;
|
|
343
|
+
settings.targets = settings.targets.filter((t) => t.id !== targetId);
|
|
344
|
+
if (settings.targets.length === initialLength) return false;
|
|
345
|
+
await store.set({ key: STORE_KEY, value: settings });
|
|
346
|
+
return true;
|
|
347
|
+
},
|
|
348
|
+
async getTarget(targetId) {
|
|
349
|
+
const settings = await this.getSettings();
|
|
350
|
+
return settings.targets?.find((t) => t.id === targetId) || null;
|
|
217
351
|
},
|
|
218
352
|
async getToken() {
|
|
219
353
|
const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
|
|
220
|
-
|
|
354
|
+
let dbSettings = await store.get({ key: STORE_KEY });
|
|
355
|
+
dbSettings = this.migrateSettings(dbSettings);
|
|
221
356
|
return dbSettings?.githubToken || null;
|
|
222
357
|
},
|
|
223
358
|
async isConfigured() {
|
|
224
359
|
const settings = await this.getSettings();
|
|
225
360
|
const hasToken = await this.getToken();
|
|
226
361
|
const { owner, repo } = this.parseRepoUrl(settings.repoUrl);
|
|
227
|
-
|
|
362
|
+
const hasTargets = settings.targets && settings.targets.length > 0;
|
|
363
|
+
return !!(owner && repo && hasToken && hasTargets);
|
|
228
364
|
},
|
|
229
365
|
async hasToken() {
|
|
230
366
|
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
|
});
|