@orange-soft/strapi-deployment-trigger 1.0.0 → 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.
@@ -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-3JntxPYv.js"));
40
+ const { App } = await Promise.resolve().then(() => require("./App-CuSCtdH7.js"));
41
41
  return App;
42
42
  }
43
43
  });
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
- const index = require("../_chunks/index-CqpMwL_C.js");
2
+ const index = require("../_chunks/index-DGatQB-9.js");
3
3
  module.exports = index.index;
@@ -1,4 +1,4 @@
1
- import { i } from "../_chunks/index-C18aSW5z.mjs";
1
+ import { i } from "../_chunks/index-CZWWYGR3.mjs";
2
2
  export {
3
3
  i as default
4
4
  };
@@ -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 = settings.workflow, branch = settings.branch } = ctx.request.body || {};
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
- workflow: "deploy.yml",
156
- branch: "master"
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,35 +245,43 @@ const service = ({ strapi }) => ({
177
245
  }
178
246
  return { owner: null, repo: null };
179
247
  },
180
- async getSettings() {
181
- const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
182
- const dbSettings = await store.get({ key: STORE_KEY });
183
- const envToken = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
184
- if (dbSettings && dbSettings.repoUrl) {
185
- const token2 = dbSettings.githubToken || envToken;
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) {
186
255
  return {
187
- ...dbSettings,
188
- githubToken: void 0,
189
- // Never expose full token
190
- hasToken: !!token2,
191
- maskedToken: this.maskToken(token2)
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
+ }]
192
264
  };
193
265
  }
194
- const pluginConfig = strapi.config.get(`plugin::${PLUGIN_ID}`) || {};
195
- if (pluginConfig.githubOwner && pluginConfig.githubRepo) {
266
+ return settings;
267
+ },
268
+ async getSettings() {
269
+ const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
270
+ let dbSettings = await store.get({ key: STORE_KEY });
271
+ dbSettings = this.migrateSettings(dbSettings);
272
+ if (!dbSettings) {
196
273
  return {
197
- repoUrl: `https://github.com/${pluginConfig.githubOwner}/${pluginConfig.githubRepo}`,
198
- workflow: pluginConfig.workflow || DEFAULT_SETTINGS.workflow,
199
- branch: pluginConfig.branch || DEFAULT_SETTINGS.branch,
200
- hasToken: !!envToken,
201
- maskedToken: this.maskToken(envToken)
274
+ ...DEFAULT_SETTINGS,
275
+ hasToken: false,
276
+ maskedToken: null
202
277
  };
203
278
  }
204
- const token = dbSettings?.githubToken || envToken;
279
+ const token = dbSettings.githubToken;
205
280
  return {
206
- ...DEFAULT_SETTINGS,
207
- ...dbSettings,
281
+ repoUrl: dbSettings.repoUrl || "",
282
+ targets: dbSettings.targets || [],
208
283
  githubToken: void 0,
284
+ // Never expose full token
209
285
  hasToken: !!token,
210
286
  maskedToken: this.maskToken(token)
211
287
  };
@@ -213,33 +289,79 @@ const service = ({ strapi }) => ({
213
289
  async saveSettings(settings) {
214
290
  const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
215
291
  const existingSettings = await store.get({ key: STORE_KEY }) || {};
292
+ const migrated = this.migrateSettings(existingSettings);
216
293
  const settingsToSave = {
217
- repoUrl: settings.repoUrl,
218
- workflow: settings.workflow || DEFAULT_SETTINGS.workflow,
219
- 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 || [],
220
296
  // Only update token if a new one is provided
221
- githubToken: settings.githubToken || existingSettings.githubToken
297
+ githubToken: settings.githubToken || migrated?.githubToken || ""
222
298
  };
223
299
  await store.set({ key: STORE_KEY, value: settingsToSave });
224
300
  return {
225
- ...settingsToSave,
301
+ repoUrl: settingsToSave.repoUrl,
302
+ targets: settingsToSave.targets,
226
303
  githubToken: void 0,
227
- hasToken: !!settingsToSave.githubToken || !!process.env.GITHUB_PERSONAL_ACCESS_TOKEN
304
+ hasToken: !!settingsToSave.githubToken,
305
+ maskedToken: this.maskToken(settingsToSave.githubToken)
228
306
  };
229
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"
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;
352
+ },
230
353
  async getToken() {
231
354
  const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
232
- const dbSettings = await store.get({ key: STORE_KEY });
233
- if (dbSettings?.githubToken) {
234
- return dbSettings.githubToken;
235
- }
236
- return process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
355
+ let dbSettings = await store.get({ key: STORE_KEY });
356
+ dbSettings = this.migrateSettings(dbSettings);
357
+ return dbSettings?.githubToken || null;
237
358
  },
238
359
  async isConfigured() {
239
360
  const settings = await this.getSettings();
240
361
  const hasToken = await this.getToken();
241
362
  const { owner, repo } = this.parseRepoUrl(settings.repoUrl);
242
- return !!(owner && repo && hasToken);
363
+ const hasTargets = settings.targets && settings.targets.length > 0;
364
+ return !!(owner && repo && hasToken && hasTargets);
243
365
  },
244
366
  async hasToken() {
245
367
  const token = await this.getToken();
@@ -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 = settings.workflow, branch = settings.branch } = ctx.request.body || {};
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
- workflow: "deploy.yml",
155
- branch: "master"
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,35 +244,43 @@ const service = ({ strapi }) => ({
176
244
  }
177
245
  return { owner: null, repo: null };
178
246
  },
179
- async getSettings() {
180
- const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
181
- const dbSettings = await store.get({ key: STORE_KEY });
182
- const envToken = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
183
- if (dbSettings && dbSettings.repoUrl) {
184
- const token2 = dbSettings.githubToken || envToken;
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) {
185
254
  return {
186
- ...dbSettings,
187
- githubToken: void 0,
188
- // Never expose full token
189
- hasToken: !!token2,
190
- maskedToken: this.maskToken(token2)
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
+ }]
191
263
  };
192
264
  }
193
- const pluginConfig = strapi.config.get(`plugin::${PLUGIN_ID}`) || {};
194
- if (pluginConfig.githubOwner && pluginConfig.githubRepo) {
265
+ return settings;
266
+ },
267
+ async getSettings() {
268
+ const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
269
+ let dbSettings = await store.get({ key: STORE_KEY });
270
+ dbSettings = this.migrateSettings(dbSettings);
271
+ if (!dbSettings) {
195
272
  return {
196
- repoUrl: `https://github.com/${pluginConfig.githubOwner}/${pluginConfig.githubRepo}`,
197
- workflow: pluginConfig.workflow || DEFAULT_SETTINGS.workflow,
198
- branch: pluginConfig.branch || DEFAULT_SETTINGS.branch,
199
- hasToken: !!envToken,
200
- maskedToken: this.maskToken(envToken)
273
+ ...DEFAULT_SETTINGS,
274
+ hasToken: false,
275
+ maskedToken: null
201
276
  };
202
277
  }
203
- const token = dbSettings?.githubToken || envToken;
278
+ const token = dbSettings.githubToken;
204
279
  return {
205
- ...DEFAULT_SETTINGS,
206
- ...dbSettings,
280
+ repoUrl: dbSettings.repoUrl || "",
281
+ targets: dbSettings.targets || [],
207
282
  githubToken: void 0,
283
+ // Never expose full token
208
284
  hasToken: !!token,
209
285
  maskedToken: this.maskToken(token)
210
286
  };
@@ -212,33 +288,79 @@ const service = ({ strapi }) => ({
212
288
  async saveSettings(settings) {
213
289
  const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
214
290
  const existingSettings = await store.get({ key: STORE_KEY }) || {};
291
+ const migrated = this.migrateSettings(existingSettings);
215
292
  const settingsToSave = {
216
- repoUrl: settings.repoUrl,
217
- workflow: settings.workflow || DEFAULT_SETTINGS.workflow,
218
- 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 || [],
219
295
  // Only update token if a new one is provided
220
- githubToken: settings.githubToken || existingSettings.githubToken
296
+ githubToken: settings.githubToken || migrated?.githubToken || ""
221
297
  };
222
298
  await store.set({ key: STORE_KEY, value: settingsToSave });
223
299
  return {
224
- ...settingsToSave,
300
+ repoUrl: settingsToSave.repoUrl,
301
+ targets: settingsToSave.targets,
225
302
  githubToken: void 0,
226
- hasToken: !!settingsToSave.githubToken || !!process.env.GITHUB_PERSONAL_ACCESS_TOKEN
303
+ hasToken: !!settingsToSave.githubToken,
304
+ maskedToken: this.maskToken(settingsToSave.githubToken)
227
305
  };
228
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"
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;
351
+ },
229
352
  async getToken() {
230
353
  const store = strapi.store({ type: "plugin", name: PLUGIN_ID });
231
- const dbSettings = await store.get({ key: STORE_KEY });
232
- if (dbSettings?.githubToken) {
233
- return dbSettings.githubToken;
234
- }
235
- return process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
354
+ let dbSettings = await store.get({ key: STORE_KEY });
355
+ dbSettings = this.migrateSettings(dbSettings);
356
+ return dbSettings?.githubToken || null;
236
357
  },
237
358
  async isConfigured() {
238
359
  const settings = await this.getSettings();
239
360
  const hasToken = await this.getToken();
240
361
  const { owner, repo } = this.parseRepoUrl(settings.repoUrl);
241
- return !!(owner && repo && hasToken);
362
+ const hasTargets = settings.targets && settings.targets.length > 0;
363
+ return !!(owner && repo && hasToken && hasTargets);
242
364
  },
243
365
  async hasToken() {
244
366
  const token = await this.getToken();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orange-soft/strapi-deployment-trigger",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Strapi v5 plugin to trigger GitHub Actions workflow deployments from the admin panel",
5
5
  "license": "MIT",
6
6
  "author": "Justin Moh <moh@os.my>",
@@ -30,11 +30,45 @@ const controller = ({ strapi }) => ({
30
30
  configured,
31
31
  hasToken,
32
32
  settings,
33
- parsed: { owner, repo }, // Include parsed values for display
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
- const { workflow = settings.workflow, branch = settings.branch } = ctx.request.body || {};
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,