tsoft-cli 1.0.2 → 1.1.1
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/bin/index.js +29 -0
- package/package.json +7 -7
- package/src/client-credentials.js +87 -0
- package/src/commands/create.js +61 -0
- package/src/commands/save.js +17 -0
- package/src/commands/start.js +272 -0
- package/src/constants.js +16 -0
- package/src/file-manager.js +33 -0
- package/src/request.js +16 -0
- package/src/theme-manager.js +74 -0
- package/src/tsoft.js +110 -0
- package/bin/tsoft-cli.js +0 -107
- package/src/common.js +0 -117
package/bin/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const { start } = require('../src/commands/start');
|
|
5
|
+
const { save } = require('../src/commands/save');
|
|
6
|
+
const { createTheme } = require('../src/commands/create');
|
|
7
|
+
|
|
8
|
+
program
|
|
9
|
+
.command('start')
|
|
10
|
+
.description('Start development environment for a selected site and theme.')
|
|
11
|
+
.action(() => {
|
|
12
|
+
start();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.command('save <site> <theme>')
|
|
17
|
+
.description('Saving the file changes to the server.')
|
|
18
|
+
.action((site, theme) => {
|
|
19
|
+
save(site, theme);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
program
|
|
23
|
+
.command('create')
|
|
24
|
+
.description('Create a new theme')
|
|
25
|
+
.action(() => {
|
|
26
|
+
createTheme();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsoft-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "A theme development cli for developers who want to create themes for T-Soft.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
"author": "",
|
|
11
11
|
"license": "ISC",
|
|
12
12
|
"bin": {
|
|
13
|
-
"tsoft-cli": "./bin/
|
|
13
|
+
"tsoft-cli": "./bin/index.js"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"axios": "^1.7.2",
|
|
17
16
|
"chokidar": "^3.6.0",
|
|
17
|
+
"cli-progress": "^3.12.0",
|
|
18
18
|
"commander": "^12.1.0",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
19
|
+
"enquirer": "^2.4.1",
|
|
20
|
+
"figlet": "^1.7.0",
|
|
21
|
+
"fs": "^0.0.1-security"
|
|
22
22
|
},
|
|
23
23
|
"type": "commonjs"
|
|
24
24
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const buildPostRequest = require('./request');
|
|
2
|
+
|
|
3
|
+
class ClientCredentials {
|
|
4
|
+
site;
|
|
5
|
+
clientId;
|
|
6
|
+
clientSecret;
|
|
7
|
+
bearer;
|
|
8
|
+
|
|
9
|
+
constructor(site) {
|
|
10
|
+
this.site = site;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getEnvironment() {
|
|
14
|
+
return {
|
|
15
|
+
site: this.site,
|
|
16
|
+
clientId: this.clientId,
|
|
17
|
+
clientSecret: this.clientSecret,
|
|
18
|
+
bearer: this.bearer
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set clientId(clientId) {
|
|
23
|
+
this.clientId = clientId;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get clientId() {
|
|
27
|
+
return this.clientId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setClientSecret(clientSecret) {
|
|
31
|
+
this.clientSecret = clientSecret;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async register(CLIENT_UUID) {
|
|
36
|
+
|
|
37
|
+
const uri = "api/v3/admin/auth/auth-application";
|
|
38
|
+
const result = await this.buildPostRequest(uri, {
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
uuid: CLIENT_UUID,
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (result.status === true) {
|
|
45
|
+
this.clientId = result.data.public_key;
|
|
46
|
+
} else {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async authorize() {
|
|
54
|
+
const uri = "api/v3/admin/auth/auth-application/authorize";
|
|
55
|
+
await this.buildPostRequest(uri, {
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
public_key: this.clientId,
|
|
58
|
+
secret: this.clientSecret
|
|
59
|
+
})
|
|
60
|
+
}).then((result) => {
|
|
61
|
+
if (result.status === true) {
|
|
62
|
+
this.bearer = result.data.token.plainTextToken
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async buildPostRequest(uri, options = {}) {
|
|
70
|
+
return buildPostRequest(this.site, uri, options);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async validateToken(token) {
|
|
74
|
+
const uri = 'api/v3/admin/auth/me';
|
|
75
|
+
const response = await buildPostRequest(this.site, uri, {
|
|
76
|
+
body: JSON.stringify({
|
|
77
|
+
_method: 'GET'
|
|
78
|
+
}),
|
|
79
|
+
headers: {
|
|
80
|
+
Authorization: `Bearer ${token}`
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return response.message === 'ok'; // response.ok true ise token geçerli
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = ClientCredentials;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { Input } = require('enquirer');
|
|
4
|
+
|
|
5
|
+
async function createTheme() {
|
|
6
|
+
const sitePrompt = new Input({
|
|
7
|
+
name: 'siteName',
|
|
8
|
+
message: 'Çalışmak istediğiniz sitenin adı:'
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const themeNamePrompt = new Input({
|
|
12
|
+
name: 'themeName',
|
|
13
|
+
message: 'Tema adı:'
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const directoryNamePrompt = new Input({
|
|
17
|
+
name: 'directoryName',
|
|
18
|
+
message: 'Tema dizin ismi (a-z yalnızca İngilizce karakterler):',
|
|
19
|
+
validate: input => /^[a-z]+$/.test(input) ? true : 'Yalnızca küçük İngilizce harfler kullanabilirsiniz.'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const authorPrompt = new Input({
|
|
23
|
+
name: 'author',
|
|
24
|
+
message: 'Author (isteğe bağlı):'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const versionPrompt = new Input({
|
|
28
|
+
name: 'version',
|
|
29
|
+
message: 'Version (isteğe bağlı, varsayılan 1.0.0):',
|
|
30
|
+
initial: '1.0.0'
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const siteName = await sitePrompt.run();
|
|
34
|
+
const themeName = await themeNamePrompt.run();
|
|
35
|
+
const directoryName = await directoryNamePrompt.run();
|
|
36
|
+
const author = await authorPrompt.run();
|
|
37
|
+
const version = await versionPrompt.run();
|
|
38
|
+
|
|
39
|
+
const cwd = process.cwd();
|
|
40
|
+
const themePath = path.join(cwd, siteName, directoryName);
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(themePath)) {
|
|
43
|
+
fs.mkdirSync(themePath, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const themeData = {
|
|
47
|
+
siteName,
|
|
48
|
+
themeName,
|
|
49
|
+
directoryName,
|
|
50
|
+
author,
|
|
51
|
+
version
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
fs.writeFileSync(path.join(themePath, '.theme'), JSON.stringify(themeData, null, 2));
|
|
55
|
+
|
|
56
|
+
console.log(`Tema "${themeName}" başarıyla oluşturuldu.`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
createTheme
|
|
61
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const Tsoft = require('../tsoft');
|
|
2
|
+
|
|
3
|
+
async function save(site, theme) {
|
|
4
|
+
const t = new Tsoft(site);
|
|
5
|
+
const isValid = await t.isTokenValid(theme);
|
|
6
|
+
|
|
7
|
+
if (isValid) {
|
|
8
|
+
await t.saveOnlineStore(theme);
|
|
9
|
+
console.log('Online store saved successfully.');
|
|
10
|
+
} else {
|
|
11
|
+
console.log('Invalid Bearer token. Please re-authenticate.');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
save
|
|
17
|
+
};
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
const Tsoft = require('../tsoft');
|
|
2
|
+
const constants = require('../constants');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const {Input, Select, Confirm, prompt} = require('enquirer');
|
|
6
|
+
const chokidar = require('chokidar');
|
|
7
|
+
const figlet = require('figlet');
|
|
8
|
+
const readline = require('readline');
|
|
9
|
+
const cliProgress = require('cli-progress');
|
|
10
|
+
const {exec} = require('child_process');
|
|
11
|
+
|
|
12
|
+
async function displayAsciiArt() {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
figlet('T-SOFT', {
|
|
15
|
+
font: 'Standard',
|
|
16
|
+
}, (err, data) => {
|
|
17
|
+
if (err) {
|
|
18
|
+
console.log('Something went wrong...');
|
|
19
|
+
console.dir(err);
|
|
20
|
+
reject(err);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log(data);
|
|
24
|
+
resolve();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function showProgress(tsoft, theme) {
|
|
30
|
+
const themePath = path.join(process.cwd(), tsoft.site, theme);
|
|
31
|
+
const fileList = tsoft.fileManager.listFilesRecursive(themePath).filter(file => !file.includes('.env'));
|
|
32
|
+
const totalFiles = fileList.length;
|
|
33
|
+
|
|
34
|
+
// Create a new progress bar instance and use shades_classic theme
|
|
35
|
+
const progressBar = new cliProgress.SingleBar({
|
|
36
|
+
format: 'Progress |{bar}| {percentage}% || {value}/{total} Files Uploaded',
|
|
37
|
+
barCompleteChar: '\u2588',
|
|
38
|
+
barIncompleteChar: '\u2591',
|
|
39
|
+
hideCursor: true,
|
|
40
|
+
clearOnComplete: false
|
|
41
|
+
});
|
|
42
|
+
progressBar.start(totalFiles, 0);
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < totalFiles; i++) {
|
|
45
|
+
const file = fileList[i];
|
|
46
|
+
const fileName = tsoft.fileManager.getRelativePath(theme, file);
|
|
47
|
+
const content = tsoft.fileManager.readFile(file);
|
|
48
|
+
const env = tsoft.themeManager.getEnvironment(theme);
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
await tsoft.saveFile(theme, fileName, content, env.bearer);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(`Error uploading file ${fileName}:`, error);
|
|
55
|
+
}
|
|
56
|
+
progressBar.update(i + 1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
progressBar.stop();
|
|
60
|
+
|
|
61
|
+
return 'All files uploaded with progress shown successfully';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function updateTheme(tsoft, theme) {
|
|
65
|
+
const directoryName = path.basename(path.join(process.cwd(), tsoft.site, theme));
|
|
66
|
+
const response = await tsoft.updateTheme(directoryName);
|
|
67
|
+
if (response.status === true) {
|
|
68
|
+
return 'Successfully updated';
|
|
69
|
+
} else {
|
|
70
|
+
throw new Error('Update failed');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function clearScreen() {
|
|
75
|
+
process.stdout.write('\x1B[2J\x1B[0f');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function editThemeFile(themeManager, directoryName) {
|
|
79
|
+
const themePath = path.join(process.cwd(), themeManager.site, directoryName, '.theme');
|
|
80
|
+
|
|
81
|
+
if (!fs.existsSync(themePath)) {
|
|
82
|
+
console.error('.theme file not found.');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`Opening .theme file: ${themePath}`);
|
|
87
|
+
const openCommand = process.platform === 'win32' ? 'start' :
|
|
88
|
+
process.platform === 'darwin' ? 'open' :
|
|
89
|
+
'xdg-open';
|
|
90
|
+
|
|
91
|
+
exec(`${openCommand} ${themePath}`, (err) => {
|
|
92
|
+
if (err) {
|
|
93
|
+
console.error('Failed to open .theme file:', err);
|
|
94
|
+
} else {
|
|
95
|
+
console.log('.theme file opened successfully.');
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function info() {
|
|
101
|
+
console.log('\n' +
|
|
102
|
+
'********************************************************\n' +
|
|
103
|
+
'* *\n' +
|
|
104
|
+
'* Press "q" to quit watching, "u" to update theme, *\n' +
|
|
105
|
+
'* "s" to save the theme, "e" to edit .theme file. *\n' +
|
|
106
|
+
'* *\n' +
|
|
107
|
+
'********************************************************\n'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function start() {
|
|
112
|
+
process.stdout.write('\x1B[2J\x1B[0f');
|
|
113
|
+
await displayAsciiArt();
|
|
114
|
+
const cwd = process.cwd();
|
|
115
|
+
const sites = fs.readdirSync(cwd).filter(file => {
|
|
116
|
+
return file.charAt(0) !== '.' && !['node_modules', 'build', 'dist'].includes(file);
|
|
117
|
+
}).filter(file => fs.statSync(path.join(cwd, file)).isDirectory());
|
|
118
|
+
|
|
119
|
+
if (sites.length === 0) {
|
|
120
|
+
console.error('No sites found in the current directory.');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const sitePrompt = new Select({
|
|
125
|
+
name: 'selectedSite',
|
|
126
|
+
message: 'Please select a site:',
|
|
127
|
+
choices: sites
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const selectedSite = await sitePrompt.run();
|
|
131
|
+
|
|
132
|
+
const sitePath = path.join(cwd, selectedSite);
|
|
133
|
+
const themes = fs.readdirSync(sitePath).filter(file => fs.statSync(path.join(sitePath, file)).isDirectory());
|
|
134
|
+
|
|
135
|
+
if (themes.length === 0) {
|
|
136
|
+
console.error('No themes found in the selected site directory.');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const themePrompt = new Select({
|
|
141
|
+
name: 'selectedTheme',
|
|
142
|
+
message: 'Please select a theme:',
|
|
143
|
+
choices: themes
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const selectedTheme = await themePrompt.run();
|
|
147
|
+
const themeEnvPath = path.join(sitePath, selectedTheme, `${selectedTheme}.env`);
|
|
148
|
+
|
|
149
|
+
// Check if .env file exists and contains a valid JSON with a bearer attribute
|
|
150
|
+
let envData = {};
|
|
151
|
+
if (fs.existsSync(themeEnvPath)) {
|
|
152
|
+
try {
|
|
153
|
+
envData = JSON.parse(fs.readFileSync(themeEnvPath, 'utf-8'));
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.warn(`Invalid JSON format in ${themeEnvPath}. This file will be deleted and you will need to reauthorize.`);
|
|
156
|
+
fs.unlinkSync(themeEnvPath);
|
|
157
|
+
console.info('Invalid environment file deleted. Proceeding with initialization...');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (envData.bearer) {
|
|
161
|
+
const tsoft = new Tsoft(selectedSite);
|
|
162
|
+
const bearerTokenValid = await tsoft.isTokenValid(selectedTheme);
|
|
163
|
+
|
|
164
|
+
if (bearerTokenValid) {
|
|
165
|
+
console.info('Existing environment file with a valid bearer token found. Proceeding with existing configuration...');
|
|
166
|
+
// Start watching for file changes
|
|
167
|
+
await startWatching(selectedSite, selectedTheme, tsoft);
|
|
168
|
+
return;
|
|
169
|
+
} else {
|
|
170
|
+
console.warn('Existing bearer token is invalid. You will need to reauthorize.');
|
|
171
|
+
fs.unlinkSync(themeEnvPath);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let t = new Tsoft(selectedSite);
|
|
177
|
+
const initializeResult = await t.initialize(selectedTheme, {reset: false});
|
|
178
|
+
|
|
179
|
+
const approvalPrompt = new Input({
|
|
180
|
+
name: 'approval',
|
|
181
|
+
message: 'Please provide the approval code:'
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const approval = await approvalPrompt.run();
|
|
185
|
+
|
|
186
|
+
if (approval) {
|
|
187
|
+
await constants.APPROVAL.handler(t.clientCredentials.setClientSecret(approval));
|
|
188
|
+
t.buildEnvironment();
|
|
189
|
+
console.info('You are ready to go');
|
|
190
|
+
|
|
191
|
+
// Start watching for file changes
|
|
192
|
+
await startWatching(selectedSite, selectedTheme, t);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function startWatching(site, theme, tsoft) {
|
|
197
|
+
const themePath = path.join(process.cwd(), site, theme);
|
|
198
|
+
const envPath = path.join(themePath, `${theme}.env`);
|
|
199
|
+
|
|
200
|
+
if (!fs.existsSync(envPath)) {
|
|
201
|
+
console.error('Environment file not found. Please initialize the theme first.');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const watcher = chokidar.watch(themePath, {
|
|
206
|
+
ignored: /(^|[\/\\])\../, // ignore dotfiles
|
|
207
|
+
persistent: true
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
watcher.on('change', async (filePath) => {
|
|
211
|
+
console.log(`File ${filePath} has been changed`);
|
|
212
|
+
|
|
213
|
+
const env = JSON.parse(fs.readFileSync(envPath, 'utf-8'));
|
|
214
|
+
const fileName = filePath.replace(`${themePath}/`, '');
|
|
215
|
+
const content = fs.readFileSync(filePath).toString();
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const result = await tsoft.saveFile(theme, fileName, content, env.bearer);
|
|
219
|
+
console.log(`File ${fileName} uploaded successfully`);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error(`Error uploading file ${fileName}:`, error);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
console.log(`Watching for file changes in ${themePath}`);
|
|
226
|
+
|
|
227
|
+
const rl = readline.createInterface({
|
|
228
|
+
input: process.stdin,
|
|
229
|
+
output: process.stdout
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
info();
|
|
233
|
+
|
|
234
|
+
rl.on('line', async (input) => {
|
|
235
|
+
if (input.trim() === 'q') {
|
|
236
|
+
console.log('Quitting file watch...');
|
|
237
|
+
watcher.close();
|
|
238
|
+
rl.close();
|
|
239
|
+
process.exit(0);
|
|
240
|
+
} else if (input.trim() === 'u') {
|
|
241
|
+
console.log('Updating theme...');
|
|
242
|
+
try {
|
|
243
|
+
const result = await updateTheme(tsoft, theme);
|
|
244
|
+
console.log(result);
|
|
245
|
+
info();
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error(`Error updating theme:`, error);
|
|
248
|
+
}
|
|
249
|
+
} else if (input.trim() === 's') {
|
|
250
|
+
clearScreen();
|
|
251
|
+
console.log('Starting to upload all files with progress...');
|
|
252
|
+
try {
|
|
253
|
+
const result = await showProgress(tsoft, theme);
|
|
254
|
+
console.log(result);
|
|
255
|
+
info();
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(`Error showing progress:`, error);
|
|
258
|
+
}
|
|
259
|
+
} else if (input.trim() === 'e') {
|
|
260
|
+
console.log('Editing .theme file...');
|
|
261
|
+
try {
|
|
262
|
+
await editThemeFile(tsoft.themeManager, theme);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error(`Error editing .theme file:`, error);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
module.exports = {
|
|
271
|
+
start
|
|
272
|
+
};
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const handleAuthorization = async (credentialHandler) => {
|
|
2
|
+
await credentialHandler.authorize();
|
|
3
|
+
};
|
|
4
|
+
module.exports = Object.freeze({
|
|
5
|
+
INITIALIZE: {
|
|
6
|
+
type: 'input',
|
|
7
|
+
name: 'site',
|
|
8
|
+
message: 'Lütfen site adresini giriniz (örnek: https://tsoft.com.tr):'
|
|
9
|
+
},
|
|
10
|
+
APPROVAL: {
|
|
11
|
+
type: 'input',
|
|
12
|
+
name: 'approval',
|
|
13
|
+
message: 'Parolanızı giriniz (Lütfen panelden onaylayınız):',
|
|
14
|
+
handler: handleAuthorization
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class FileManager {
|
|
5
|
+
constructor(site) {
|
|
6
|
+
this.site = site;
|
|
7
|
+
this.cwd = process.cwd();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
listFilesRecursive(directory, fileList = []) {
|
|
11
|
+
const files = fs.readdirSync(directory);
|
|
12
|
+
files.forEach(file => {
|
|
13
|
+
const fullPath = path.join(directory, file);
|
|
14
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
15
|
+
this.listFilesRecursive(fullPath, fileList);
|
|
16
|
+
} else {
|
|
17
|
+
fileList.push(fullPath);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return fileList;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getRelativePath(theme, file) {
|
|
24
|
+
const themePath = path.join(this.cwd, this.site, theme);
|
|
25
|
+
return file.replace(`${themePath}/`, '');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
readFile(file) {
|
|
29
|
+
return fs.readFileSync(file).toString();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = FileManager;
|
package/src/request.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = (site, uri, options = {}) => {
|
|
2
|
+
return fetch("https://" + site + '/' + uri, {
|
|
3
|
+
method: 'POST',
|
|
4
|
+
headers: {
|
|
5
|
+
'Content-Type': 'application/json',
|
|
6
|
+
'Accept': 'application/json',
|
|
7
|
+
...options.headers ?? {}
|
|
8
|
+
},
|
|
9
|
+
body: options.body
|
|
10
|
+
})
|
|
11
|
+
.then((response) => response.json())
|
|
12
|
+
.then((result) => {
|
|
13
|
+
return result
|
|
14
|
+
})
|
|
15
|
+
.catch((error) => console.error(error));
|
|
16
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class ThemeManager {
|
|
5
|
+
constructor(site) {
|
|
6
|
+
this.site = site;
|
|
7
|
+
this.cwd = process.cwd();
|
|
8
|
+
this.theme = null;
|
|
9
|
+
this.registered = false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async initializeTheme(theme, reInit = false) {
|
|
13
|
+
|
|
14
|
+
this.theme = theme;
|
|
15
|
+
const themePath = path.join(this.cwd, this.site, theme);
|
|
16
|
+
const themeEnvPath = path.join(themePath, `${theme}.env`);
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if (reInit) {
|
|
20
|
+
///await fs.rm(themePath, {recursive: true, force: true}, (err, file) => {});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(path.join(this.cwd, this.site))) {
|
|
24
|
+
fs.mkdirSync(path.join(this.cwd, this.site));
|
|
25
|
+
}
|
|
26
|
+
if (!fs.existsSync(themePath)) {
|
|
27
|
+
fs.mkdirSync(themePath);
|
|
28
|
+
}
|
|
29
|
+
if (reInit || !fs.existsSync(themeEnvPath)) {
|
|
30
|
+
fs.writeFileSync(themeEnvPath, '');
|
|
31
|
+
this.registered = false;
|
|
32
|
+
} else {
|
|
33
|
+
const data = fs.readFileSync(themeEnvPath);
|
|
34
|
+
try {
|
|
35
|
+
const environment = JSON.parse(data.toString());
|
|
36
|
+
this.registered = Boolean(environment.bearer);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
this.registered = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
isRegistered() {
|
|
44
|
+
return this.registered;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
buildEnvironment(environment) {
|
|
48
|
+
const envPath = path.join(this.cwd, this.site, this.theme, `${this.theme}.env`);
|
|
49
|
+
fs.writeFileSync(envPath, JSON.stringify(environment));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getEnvironment(theme) {
|
|
53
|
+
const themeEnvPath = path.join(this.cwd, this.site, theme, `${theme}.env`);
|
|
54
|
+
return JSON.parse(fs.readFileSync(themeEnvPath));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getThemeInfo(directoryName) {
|
|
58
|
+
const themePath = path.join(process.cwd(), this.site, directoryName, '.theme');
|
|
59
|
+
if (fs.existsSync(themePath)) {
|
|
60
|
+
const themeInfo = fs.readFileSync(themePath, 'utf-8');
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(themeInfo);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error('Error parsing .theme file:', error);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
console.error('.theme file not found in', themePath);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = ThemeManager;
|
package/src/tsoft.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const ClientCredentials = require('./client-credentials');
|
|
2
|
+
const constants = require('./constants');
|
|
3
|
+
const FileManager = require('./file-manager');
|
|
4
|
+
const ThemeManager = require('./theme-manager');
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const buildPostRequest = require('./request');
|
|
7
|
+
|
|
8
|
+
class Tsoft {
|
|
9
|
+
static TSOFT_CLI_UUID = '7583a318-8542-4ec8-980c-48ea67031be3';
|
|
10
|
+
static ONLINESTORE_SAVE_URI = 'api/v3/admin/online-store-2/theme/file-manager?q=save';
|
|
11
|
+
static UPDATE_THEME_URI = 'api/v3/admin/online-store-2/theme/update-theme';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
constructor(site) {
|
|
15
|
+
this.site = site;
|
|
16
|
+
this.themeManager = new ThemeManager(site);
|
|
17
|
+
this.fileManager = new FileManager(site);
|
|
18
|
+
this.clientCredentials = new ClientCredentials(site);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async initialize(theme, reInit = false) {
|
|
22
|
+
await this.themeManager.initializeTheme(theme, reInit);
|
|
23
|
+
if (!this.themeManager.isRegistered()) {
|
|
24
|
+
return await this.register(Tsoft.TSOFT_CLI_UUID);
|
|
25
|
+
} else {
|
|
26
|
+
console.warn('You are already registered');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
buildEnvironment() {
|
|
31
|
+
this.themeManager.buildEnvironment(this.clientCredentials.getEnvironment());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async authorize() {
|
|
35
|
+
await this.clientCredentials.authorize();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async register(CLIENT_UUID) {
|
|
39
|
+
const response = await this.clientCredentials.register(CLIENT_UUID);
|
|
40
|
+
return response === true ? constants.APPROVAL : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async saveOnlineStore(theme) {
|
|
44
|
+
const fileList = this.fileManager.listFilesRecursive(
|
|
45
|
+
path.join(process.cwd(), this.site, theme)
|
|
46
|
+
);
|
|
47
|
+
await Promise.all(fileList.filter(file => !file.includes('.env')).map(async file => {
|
|
48
|
+
const fileName = this.fileManager.getRelativePath(theme, file);
|
|
49
|
+
const content = this.fileManager.readFile(file);
|
|
50
|
+
const env = this.themeManager.getEnvironment(theme);
|
|
51
|
+
const result = await buildPostRequest(this.site, Tsoft.ONLINESTORE_SAVE_URI, {
|
|
52
|
+
headers: {
|
|
53
|
+
Authorization: `Bearer ${env.bearer}`
|
|
54
|
+
},
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
theme: theme,
|
|
57
|
+
path: fileName,
|
|
58
|
+
content: content
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
console.log({
|
|
62
|
+
result: result
|
|
63
|
+
})
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async saveFile(theme, fileName, content, bearerToken) {
|
|
68
|
+
const response = await buildPostRequest(this.site, Tsoft.ONLINESTORE_SAVE_URI, {
|
|
69
|
+
headers: {
|
|
70
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
71
|
+
'Content-Type': 'application/json'
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
theme: theme,
|
|
75
|
+
path: fileName,
|
|
76
|
+
content: content
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return response;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async isTokenValid(theme) {
|
|
85
|
+
const env = this.themeManager.getEnvironment(theme);
|
|
86
|
+
return await this.clientCredentials.validateToken(env.bearer);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async updateTheme(directoryName) {
|
|
90
|
+
const env = this.themeManager.getEnvironment(directoryName);
|
|
91
|
+
const themeInfo = this.themeManager.getThemeInfo(directoryName); // .theme bilgisi alınıyor
|
|
92
|
+
|
|
93
|
+
const response = await buildPostRequest(this.site, Tsoft.UPDATE_THEME_URI, {
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: `Bearer ${env.bearer}`,
|
|
96
|
+
'Content-Type': 'application/json',
|
|
97
|
+
'Accept': 'application/json'
|
|
98
|
+
},
|
|
99
|
+
body: JSON.stringify({
|
|
100
|
+
theme: directoryName,
|
|
101
|
+
themeInfo: themeInfo
|
|
102
|
+
})
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return response;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = Tsoft;
|
package/bin/tsoft-cli.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const Common = require('../src/common');
|
|
3
|
-
const { program } = require('commander');
|
|
4
|
-
const axios = require('axios');
|
|
5
|
-
const chokidar = require('chokidar');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const _ = require('lodash');
|
|
9
|
-
|
|
10
|
-
program
|
|
11
|
-
.command('init')
|
|
12
|
-
.description('Initialize the CLI')
|
|
13
|
-
.action(() => {
|
|
14
|
-
init();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
program.parse(process.argv);
|
|
18
|
-
|
|
19
|
-
function init() {
|
|
20
|
-
(async () => {
|
|
21
|
-
const {default: inquirer} = await import('inquirer');
|
|
22
|
-
|
|
23
|
-
const tsoft = new Common();
|
|
24
|
-
|
|
25
|
-
if (Common.skip !== true) {
|
|
26
|
-
await tsoft.handleStep1(
|
|
27
|
-
(await inquirer.prompt(
|
|
28
|
-
[
|
|
29
|
-
{
|
|
30
|
-
type: 'input',
|
|
31
|
-
name: 'step1',
|
|
32
|
-
message: 'Site Adresi https://(tsoft.com.tr):'
|
|
33
|
-
},
|
|
34
|
-
]
|
|
35
|
-
))
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
await tsoft.handleStep2(
|
|
39
|
-
(await inquirer.prompt(
|
|
40
|
-
[
|
|
41
|
-
{
|
|
42
|
-
type: 'input',
|
|
43
|
-
name: 'step2',
|
|
44
|
-
message: 'Parola (Panelden Onaylayın):'
|
|
45
|
-
},
|
|
46
|
-
]
|
|
47
|
-
))
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
await tsoft.handleStep3(
|
|
51
|
-
(await inquirer.prompt(
|
|
52
|
-
[
|
|
53
|
-
{
|
|
54
|
-
type: 'input',
|
|
55
|
-
name: 'step3',
|
|
56
|
-
message: 'Tema Adı:'
|
|
57
|
-
},
|
|
58
|
-
]
|
|
59
|
-
))
|
|
60
|
-
)
|
|
61
|
-
} else {
|
|
62
|
-
|
|
63
|
-
const debouncedSendFileChange = _.debounce(async (data) => {
|
|
64
|
-
try {
|
|
65
|
-
const response = await axios.post('https://'+this.site+'/api', { data });
|
|
66
|
-
console.log('API response:', response.data);
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error('Error sending API request:', error);
|
|
69
|
-
}
|
|
70
|
-
}, 1000);
|
|
71
|
-
|
|
72
|
-
const watchDirectory = path.join(__dirname, 'themes'); // İzlemek istediğiniz dizinin yolunu belirtin
|
|
73
|
-
const watcher = chokidar.watch(watchDirectory, {
|
|
74
|
-
persistent: true,
|
|
75
|
-
ignored: /(^|[\/\\])\../,
|
|
76
|
-
ignoreInitial: true,
|
|
77
|
-
followSymlinks: true,
|
|
78
|
-
depth: 99,
|
|
79
|
-
awaitWriteFinish: {
|
|
80
|
-
stabilityThreshold: 2000,
|
|
81
|
-
pollInterval: 100
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
watcher.on('all', (event, filePath) => {
|
|
86
|
-
if (event === 'change' || event === 'add' || event === 'unlink') {
|
|
87
|
-
console.log(`File ${filePath} has been ${event}`);
|
|
88
|
-
|
|
89
|
-
// Dosya değişikliğinde dosya içeriğini oku ve debounce edilmiş API'ye gönder
|
|
90
|
-
fs.readFile(filePath, 'utf8', (err, data) => {
|
|
91
|
-
if (err) {
|
|
92
|
-
console.error('Error reading file:', err);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
debouncedSendFileChange(data);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
})();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
package/src/common.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
|
|
4
|
-
class Common {
|
|
5
|
-
static CLIENT_UUID = "7583a318-8542-4ec8-980c-48ea67031be3";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
site = null;
|
|
9
|
-
public_key = null;
|
|
10
|
-
secret = null;
|
|
11
|
-
|
|
12
|
-
theme = null;
|
|
13
|
-
|
|
14
|
-
bearer = null;
|
|
15
|
-
|
|
16
|
-
static skip = false;
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
try {
|
|
20
|
-
const environment = JSON.parse(fs.readFileSync(path.join(__dirname, '.environment'), 'utf8'))
|
|
21
|
-
|
|
22
|
-
this.site = environment.site;
|
|
23
|
-
this.public_key = environment.public_key;
|
|
24
|
-
this.secret = environment.secret;
|
|
25
|
-
this.theme = environment.theme;
|
|
26
|
-
this.bearer = environment.bearer;
|
|
27
|
-
|
|
28
|
-
Common.skip = true;
|
|
29
|
-
} catch (error) {
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
handleEnv(envObject) {
|
|
35
|
-
const filePath = path.join(__dirname, '.environment');
|
|
36
|
-
|
|
37
|
-
fs.writeFile(filePath, envObject, (err) => {
|
|
38
|
-
if (err) {
|
|
39
|
-
console.error('Error writing file:', err);
|
|
40
|
-
} else {
|
|
41
|
-
console.log('File written successfully');
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async handleStep1(message) {
|
|
47
|
-
this.site = message.step1;
|
|
48
|
-
await this.authorizationRequest();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async handleStep2(message) {
|
|
52
|
-
this.secret = message.step2;
|
|
53
|
-
await this.login();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async handleStep3(message) {
|
|
57
|
-
this.theme = message.step3;
|
|
58
|
-
|
|
59
|
-
this.handleEnv(JSON.stringify({
|
|
60
|
-
site: this.site,
|
|
61
|
-
public_key: this.public_key,
|
|
62
|
-
secret: this.secret,
|
|
63
|
-
theme: this.theme,
|
|
64
|
-
bearer: this.bearer
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
async authorizationRequest() {
|
|
70
|
-
const myHeaders = new Headers();
|
|
71
|
-
myHeaders.append("Content-Type", "application/json");
|
|
72
|
-
myHeaders.append("Accept", "application/json");
|
|
73
|
-
|
|
74
|
-
const raw = JSON.stringify({
|
|
75
|
-
"uuid": Common.CLIENT_UUID,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const requestOptions = {
|
|
79
|
-
method: "POST",
|
|
80
|
-
headers: myHeaders,
|
|
81
|
-
body: raw,
|
|
82
|
-
redirect: "follow"
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
fetch("https://" + this.site + "/api/v3/admin/auth/auth-application", requestOptions)
|
|
86
|
-
.then((response) => response.json())
|
|
87
|
-
.then((result) => {
|
|
88
|
-
this.public_key = result.public_key
|
|
89
|
-
})
|
|
90
|
-
.catch((error) => console.error(error));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async login() {
|
|
94
|
-
const myHeaders = new Headers();
|
|
95
|
-
const formdata = new FormData();
|
|
96
|
-
formdata.append("public_key", this.public_key);
|
|
97
|
-
formdata.append("secret", this.secret);
|
|
98
|
-
|
|
99
|
-
const requestOptions = {
|
|
100
|
-
method: "POST",
|
|
101
|
-
headers: myHeaders,
|
|
102
|
-
body: formdata,
|
|
103
|
-
redirect: "follow"
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
fetch("https://" + this.site + "/api/v3/admin/auth/auth-application/authorize", requestOptions)
|
|
107
|
-
.then((response) => response.json())
|
|
108
|
-
.then((result) => {
|
|
109
|
-
this.bearer = result?.token?.plainTextToken;
|
|
110
|
-
})
|
|
111
|
-
.catch((error) => console.error(error));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
module.exports = Common;
|