@orange-soft/strapi-deployment-trigger 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/admin/jsconfig.json +10 -0
- package/admin/src/components/Initializer.jsx +18 -0
- package/admin/src/components/PluginIcon.jsx +5 -0
- package/admin/src/index.js +49 -0
- package/admin/src/pages/App.jsx +17 -0
- package/admin/src/pages/HomePage.jsx +241 -0
- package/admin/src/pages/SettingsPage.jsx +370 -0
- package/admin/src/pluginId.js +1 -0
- package/admin/src/translations/en.json +3 -0
- package/admin/src/utils/getTranslation.js +5 -0
- package/dist/_chunks/App-3JntxPYv.js +520 -0
- package/dist/_chunks/App-C0Byi5W1.mjs +520 -0
- package/dist/_chunks/en-BDvOU5UD.js +6 -0
- package/dist/_chunks/en-DdBZuj6F.mjs +6 -0
- package/dist/_chunks/index-C18aSW5z.mjs +70 -0
- package/dist/_chunks/index-CqpMwL_C.js +69 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/server/index.js +264 -0
- package/dist/server/index.mjs +265 -0
- package/package.json +84 -0
- package/server/jsconfig.json +10 -0
- package/server/src/bootstrap.js +5 -0
- package/server/src/config/index.js +4 -0
- package/server/src/content-types/index.js +1 -0
- package/server/src/controllers/controller.js +95 -0
- package/server/src/controllers/index.js +5 -0
- package/server/src/destroy.js +5 -0
- package/server/src/index.js +31 -0
- package/server/src/middlewares/index.js +1 -0
- package/server/src/policies/index.js +1 -0
- package/server/src/register.js +5 -0
- package/server/src/routes/admin/index.js +37 -0
- package/server/src/routes/content-api/index.js +14 -0
- package/server/src/routes/index.js +9 -0
- package/server/src/services/index.js +5 -0
- package/server/src/services/service.js +124 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const PLUGIN_ID = 'deployment-trigger';
|
|
2
|
+
|
|
3
|
+
const getService = (strapi) => strapi.plugin(PLUGIN_ID).service('service');
|
|
4
|
+
|
|
5
|
+
const controller = ({ strapi }) => ({
|
|
6
|
+
index(ctx) {
|
|
7
|
+
ctx.body = getService(strapi).getWelcomeMessage();
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
async getSettings(ctx) {
|
|
11
|
+
const settings = await getService(strapi).getSettings();
|
|
12
|
+
ctx.body = { data: settings };
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
async saveSettings(ctx) {
|
|
16
|
+
const { data } = ctx.request.body;
|
|
17
|
+
const settings = await getService(strapi).saveSettings(data);
|
|
18
|
+
ctx.body = { data: settings };
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async getStatus(ctx) {
|
|
22
|
+
const service = getService(strapi);
|
|
23
|
+
const settings = await service.getSettings();
|
|
24
|
+
const configured = await service.isConfigured();
|
|
25
|
+
const hasToken = await service.hasToken();
|
|
26
|
+
const { owner, repo } = service.parseRepoUrl(settings.repoUrl);
|
|
27
|
+
|
|
28
|
+
ctx.body = {
|
|
29
|
+
data: {
|
|
30
|
+
configured,
|
|
31
|
+
hasToken,
|
|
32
|
+
settings,
|
|
33
|
+
parsed: { owner, repo }, // Include parsed values for display
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async trigger(ctx) {
|
|
39
|
+
const service = getService(strapi);
|
|
40
|
+
const githubToken = await service.getToken();
|
|
41
|
+
|
|
42
|
+
if (!githubToken) {
|
|
43
|
+
return ctx.badRequest('GitHub token is not configured. Please add it in Settings.');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const settings = await service.getSettings();
|
|
47
|
+
const { owner, repo } = service.parseRepoUrl(settings.repoUrl);
|
|
48
|
+
|
|
49
|
+
if (!owner || !repo) {
|
|
50
|
+
return ctx.badRequest('GitHub repository URL is not configured or invalid. Please configure it in Settings.');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const { workflow = settings.workflow, branch = settings.branch } = ctx.request.body || {};
|
|
54
|
+
|
|
55
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/dispatches`;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetch(url, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
Accept: 'application/vnd.github.v3+json',
|
|
62
|
+
Authorization: `Bearer ${githubToken}`,
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({ ref: branch }),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const errorText = await response.text();
|
|
70
|
+
strapi.log.error(`GitHub API error: ${response.status} - ${errorText}`);
|
|
71
|
+
return ctx.badRequest(`GitHub API error: ${response.status} - ${errorText}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
strapi.log.info(`Deployment triggered for ${owner}/${repo} on branch ${branch}`);
|
|
75
|
+
|
|
76
|
+
const actionsUrl = `https://github.com/${owner}/${repo}/actions`;
|
|
77
|
+
|
|
78
|
+
ctx.body = {
|
|
79
|
+
data: {
|
|
80
|
+
success: true,
|
|
81
|
+
message: `Deployment triggered successfully for ${owner}/${repo}`,
|
|
82
|
+
repository: `${owner}/${repo}`,
|
|
83
|
+
workflow,
|
|
84
|
+
branch,
|
|
85
|
+
actionsUrl,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
strapi.log.error('Error triggering deployment:', error);
|
|
90
|
+
return ctx.badRequest(`Failed to trigger deployment: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export default controller;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application methods
|
|
3
|
+
*/
|
|
4
|
+
import bootstrap from './bootstrap';
|
|
5
|
+
import destroy from './destroy';
|
|
6
|
+
import register from './register';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Plugin server methods
|
|
10
|
+
*/
|
|
11
|
+
import config from './config';
|
|
12
|
+
import contentTypes from './content-types';
|
|
13
|
+
import controllers from './controllers';
|
|
14
|
+
import middlewares from './middlewares';
|
|
15
|
+
import policies from './policies';
|
|
16
|
+
import routes from './routes';
|
|
17
|
+
import services from './services';
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
bootstrap,
|
|
21
|
+
destroy,
|
|
22
|
+
register,
|
|
23
|
+
|
|
24
|
+
config,
|
|
25
|
+
controllers,
|
|
26
|
+
contentTypes,
|
|
27
|
+
middlewares,
|
|
28
|
+
policies,
|
|
29
|
+
routes,
|
|
30
|
+
services,
|
|
31
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export default () => ({
|
|
2
|
+
type: 'admin',
|
|
3
|
+
routes: [
|
|
4
|
+
{
|
|
5
|
+
method: 'GET',
|
|
6
|
+
path: '/settings',
|
|
7
|
+
handler: 'controller.getSettings',
|
|
8
|
+
config: {
|
|
9
|
+
policies: [],
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
method: 'PUT',
|
|
14
|
+
path: '/settings',
|
|
15
|
+
handler: 'controller.saveSettings',
|
|
16
|
+
config: {
|
|
17
|
+
policies: [],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
method: 'GET',
|
|
22
|
+
path: '/status',
|
|
23
|
+
handler: 'controller.getStatus',
|
|
24
|
+
config: {
|
|
25
|
+
policies: [],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
method: 'POST',
|
|
30
|
+
path: '/trigger',
|
|
31
|
+
handler: 'controller.trigger',
|
|
32
|
+
config: {
|
|
33
|
+
policies: [],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const PLUGIN_ID = 'deployment-trigger';
|
|
2
|
+
const STORE_KEY = 'settings';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_SETTINGS = {
|
|
5
|
+
repoUrl: '',
|
|
6
|
+
workflow: 'deploy.yml',
|
|
7
|
+
branch: 'master',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const service = ({ strapi }) => ({
|
|
11
|
+
getWelcomeMessage() {
|
|
12
|
+
return 'Welcome to Strapi Deployment Trigger';
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
maskToken(token) {
|
|
16
|
+
if (!token || token.length < 20) return null;
|
|
17
|
+
const head = token.slice(0, 15);
|
|
18
|
+
const tail = token.slice(-4);
|
|
19
|
+
return `${head}...${tail}`;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
parseRepoUrl(repoUrl) {
|
|
23
|
+
if (!repoUrl) return { owner: null, repo: null };
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(repoUrl);
|
|
26
|
+
const parts = url.pathname.split('/').filter(Boolean);
|
|
27
|
+
if (parts.length >= 2) {
|
|
28
|
+
return { owner: parts[0], repo: parts[1] };
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {
|
|
31
|
+
// Invalid URL
|
|
32
|
+
}
|
|
33
|
+
return { owner: null, repo: null };
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
async getSettings() {
|
|
37
|
+
// 1. Try database (core store)
|
|
38
|
+
const store = strapi.store({ type: 'plugin', name: PLUGIN_ID });
|
|
39
|
+
const dbSettings = await store.get({ key: STORE_KEY });
|
|
40
|
+
const envToken = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
|
|
41
|
+
|
|
42
|
+
if (dbSettings && dbSettings.repoUrl) {
|
|
43
|
+
const token = dbSettings.githubToken || envToken;
|
|
44
|
+
return {
|
|
45
|
+
...dbSettings,
|
|
46
|
+
githubToken: undefined, // Never expose full token
|
|
47
|
+
hasToken: !!token,
|
|
48
|
+
maskedToken: this.maskToken(token),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 2. Fallback to plugin config (convert old format if needed)
|
|
53
|
+
const pluginConfig = strapi.config.get(`plugin::${PLUGIN_ID}`) || {};
|
|
54
|
+
if (pluginConfig.githubOwner && pluginConfig.githubRepo) {
|
|
55
|
+
return {
|
|
56
|
+
repoUrl: `https://github.com/${pluginConfig.githubOwner}/${pluginConfig.githubRepo}`,
|
|
57
|
+
workflow: pluginConfig.workflow || DEFAULT_SETTINGS.workflow,
|
|
58
|
+
branch: pluginConfig.branch || DEFAULT_SETTINGS.branch,
|
|
59
|
+
hasToken: !!envToken,
|
|
60
|
+
maskedToken: this.maskToken(envToken),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 3. Return defaults merged with any partial DB settings
|
|
65
|
+
const token = dbSettings?.githubToken || envToken;
|
|
66
|
+
return {
|
|
67
|
+
...DEFAULT_SETTINGS,
|
|
68
|
+
...dbSettings,
|
|
69
|
+
githubToken: undefined,
|
|
70
|
+
hasToken: !!token,
|
|
71
|
+
maskedToken: this.maskToken(token),
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
async saveSettings(settings) {
|
|
76
|
+
const store = strapi.store({ type: 'plugin', name: PLUGIN_ID });
|
|
77
|
+
|
|
78
|
+
// Get existing settings to preserve token if not provided
|
|
79
|
+
const existingSettings = await store.get({ key: STORE_KEY }) || {};
|
|
80
|
+
|
|
81
|
+
const settingsToSave = {
|
|
82
|
+
repoUrl: settings.repoUrl,
|
|
83
|
+
workflow: settings.workflow || DEFAULT_SETTINGS.workflow,
|
|
84
|
+
branch: settings.branch || DEFAULT_SETTINGS.branch,
|
|
85
|
+
// Only update token if a new one is provided
|
|
86
|
+
githubToken: settings.githubToken || existingSettings.githubToken,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
await store.set({ key: STORE_KEY, value: settingsToSave });
|
|
90
|
+
|
|
91
|
+
// Return without exposing token
|
|
92
|
+
return {
|
|
93
|
+
...settingsToSave,
|
|
94
|
+
githubToken: undefined,
|
|
95
|
+
hasToken: !!settingsToSave.githubToken || !!process.env.GITHUB_PERSONAL_ACCESS_TOKEN,
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async getToken() {
|
|
100
|
+
// Priority: DB token > Environment variable
|
|
101
|
+
const store = strapi.store({ type: 'plugin', name: PLUGIN_ID });
|
|
102
|
+
const dbSettings = await store.get({ key: STORE_KEY });
|
|
103
|
+
|
|
104
|
+
if (dbSettings?.githubToken) {
|
|
105
|
+
return dbSettings.githubToken;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
async isConfigured() {
|
|
112
|
+
const settings = await this.getSettings();
|
|
113
|
+
const hasToken = await this.getToken();
|
|
114
|
+
const { owner, repo } = this.parseRepoUrl(settings.repoUrl);
|
|
115
|
+
return !!(owner && repo && hasToken);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async hasToken() {
|
|
119
|
+
const token = await this.getToken();
|
|
120
|
+
return !!token;
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export default service;
|