tsoft-cli 1.4.2 → 2.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/.editorconfig +11 -0
- package/.github/workflows/node.js.yml +25 -0
- package/.nvmrc +1 -0
- package/.prettierrc.json +8 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/settings.json +4 -0
- package/CHANGELOG.md +35 -4
- package/package.json +14 -25
- package/packages/cli/README.md +53 -0
- package/packages/cli/bin/dev.js +5 -0
- package/packages/cli/bin/run.cmd +3 -0
- package/packages/cli/bin/run.js +5 -0
- package/packages/cli/enums.ts +4 -0
- package/packages/cli/index.ts +11 -0
- package/packages/cli/package.json +59 -0
- package/packages/cli/tsconfig.json +9 -0
- package/packages/shared/index.ts +6 -0
- package/packages/shared/package.json +45 -0
- package/packages/shared/src/cli-progress.ts +3 -0
- package/packages/shared/src/commands/cli-all-commands.ts +6 -0
- package/packages/shared/src/commands/version-command.ts +12 -0
- package/packages/shared/src/consola.ts +1 -0
- package/packages/shared/src/open.ts +3 -0
- package/packages/shared/src/unzip.ts +15 -0
- package/packages/shared/tsconfig.json +9 -0
- package/packages/theme/bin/dev.js +5 -0
- package/packages/theme/bin/run.cmd +3 -0
- package/packages/theme/bin/run.js +5 -0
- package/packages/theme/index.ts +21 -0
- package/packages/theme/jest.config.ts +3 -0
- package/packages/theme/package.json +66 -0
- package/packages/theme/src/commands/theme/create.ts +51 -0
- package/packages/theme/src/commands/theme/delete-site.ts +30 -0
- package/packages/theme/src/commands/theme/delete-theme.ts +39 -0
- package/packages/theme/src/commands/theme/dev.ts +47 -0
- package/packages/theme/src/commands/theme/pull.ts +26 -0
- package/packages/theme/src/enums.ts +55 -0
- package/packages/theme/src/services/ClientCredentials.ts +127 -0
- package/packages/theme/src/services/Constants.ts +19 -0
- package/packages/theme/src/services/CreateTheme.ts +88 -0
- package/packages/theme/src/services/DeleteSite.ts +30 -0
- package/packages/theme/src/services/DeleteTheme.ts +46 -0
- package/packages/theme/src/services/FileManager.ts +62 -0
- package/packages/theme/src/services/Request.ts +18 -0
- package/packages/theme/src/services/StartTheme.ts +363 -0
- package/packages/theme/src/services/SyncTheme.ts +108 -0
- package/packages/theme/src/services/ThemeManager.ts +118 -0
- package/packages/theme/src/services/Tsoft.ts +151 -0
- package/packages/theme/src/services/types.ts +90 -0
- package/packages/theme/src/types.ts +20 -0
- package/packages/theme/src/utils/screen.ts +28 -0
- package/packages/theme/src/utils/utils.ts +83 -0
- package/packages/theme/tests/FileManager.test.js +90 -0
- package/packages/theme/tsconfig.json +9 -0
- package/pnpm-workspace.yaml +2 -0
- package/pull_request_template.md +5 -0
- package/scripts/prebuild.js +45 -0
- package/scripts/versions.js +70 -0
- package/tsconfig.build.json +21 -0
- package/bin/index.js +0 -77
- package/jest.config.js +0 -3
- package/src/Tsoft.js +0 -137
- package/src/commands/CreateCommand.js +0 -102
- package/src/commands/StartCommand.js +0 -280
- package/src/commands/SyncCommand.js +0 -97
- package/src/core/ClientCredentials.js +0 -107
- package/src/core/Constants.js +0 -19
- package/src/core/FileManager.js +0 -53
- package/src/core/Request.js +0 -41
- package/src/core/ThemeManager.js +0 -113
- package/src/core/Utils.js +0 -104
- package/tests/FileManager.test.js +0 -90
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { exec } from 'node:child_process'
|
|
4
|
+
import { openBrowser, unZIP, cliProgress, consola } from '@tsoft-cli/shared'
|
|
5
|
+
import chokidar from 'chokidar'
|
|
6
|
+
import type { FSWatcher } from 'chokidar'
|
|
7
|
+
import { input } from '@inquirer/prompts'
|
|
8
|
+
import type { Tsoft } from './Tsoft.js'
|
|
9
|
+
import { Constants } from './Constants.js'
|
|
10
|
+
import type { TThemeEnviroments } from '../types.js'
|
|
11
|
+
import { themeFileNames, themeTypes } from '../enums.js'
|
|
12
|
+
import { getFolderFromCWD, getOpenFilePlatformCode, saveThemeData, setPathForWebhookOperations } from '../utils/utils.js'
|
|
13
|
+
import { clearScreen, displayAsciiArt } from '../utils/screen.js'
|
|
14
|
+
|
|
15
|
+
export class StartTheme {
|
|
16
|
+
private tsoft: Tsoft
|
|
17
|
+
private themeName: string
|
|
18
|
+
private watcher: FSWatcher
|
|
19
|
+
|
|
20
|
+
constructor(TS: Tsoft) {
|
|
21
|
+
this.tsoft = TS
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async showProgress(): Promise<string> {
|
|
25
|
+
const themePath = getFolderFromCWD(this.tsoft.site, this.themeName)
|
|
26
|
+
const fileList = this.tsoft.fileManager
|
|
27
|
+
.listFilesRecursive(themePath)
|
|
28
|
+
.filter((file: string) => !file.includes(themeFileNames.env) && !file.includes(themeFileNames.themeTxt))
|
|
29
|
+
fileList.push(path.join(themePath, themeFileNames.theme))
|
|
30
|
+
const totalFiles = fileList.length
|
|
31
|
+
|
|
32
|
+
const progressBar = new cliProgress.SingleBar({
|
|
33
|
+
format: 'İlerleme |{bar}| {percentage}% || {value}/{total} Dosya Yüklendi',
|
|
34
|
+
barCompleteChar: '\u2588',
|
|
35
|
+
barIncompleteChar: '\u2591',
|
|
36
|
+
hideCursor: true,
|
|
37
|
+
clearOnComplete: true,
|
|
38
|
+
})
|
|
39
|
+
progressBar.start(totalFiles, 0)
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < totalFiles; i++) {
|
|
42
|
+
const file = fileList[i]
|
|
43
|
+
const fileName = this.tsoft.fileManager.getRelativePath(this.themeName, file)
|
|
44
|
+
const content = this.tsoft.fileManager.readFile(file)
|
|
45
|
+
const env = this.tsoft.themeManager.getEnvironment(this.themeName)
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await this.tsoft.saveFile(this.themeName, fileName, content, env.bearer)
|
|
49
|
+
} catch (error) {
|
|
50
|
+
consola.error(`Dosya yükleme hatası ${fileName}:`, error)
|
|
51
|
+
}
|
|
52
|
+
progressBar.update(i + 1)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
progressBar.stop()
|
|
56
|
+
|
|
57
|
+
return 'Tüm dosyalar başarıyla yüklendi'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private async initializeAndAuthorize(): Promise<boolean> {
|
|
61
|
+
await this.tsoft.initialize(this.themeName)
|
|
62
|
+
|
|
63
|
+
const approval = await input({
|
|
64
|
+
message: 'Lütfen onay kodunu sağlayın:',
|
|
65
|
+
required: true,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
if (approval) {
|
|
69
|
+
await Constants.APPROVAL.handler(this.tsoft.clientCredentials.setClientSecret(approval))
|
|
70
|
+
this.tsoft.buildEnvironment()
|
|
71
|
+
consola.success('Ortam dosyası başarıyla oluşturuldu.')
|
|
72
|
+
return true
|
|
73
|
+
} else {
|
|
74
|
+
consola.error('Onay kodu gerekli.')
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private async handleExistingEnvironment(themeEnvPath: string): Promise<boolean> {
|
|
80
|
+
let envData: TThemeEnviroments = {} as any
|
|
81
|
+
|
|
82
|
+
if (fs.existsSync(themeEnvPath)) {
|
|
83
|
+
try {
|
|
84
|
+
envData = JSON.parse(fs.readFileSync(themeEnvPath, 'utf-8'))
|
|
85
|
+
} catch (error) {
|
|
86
|
+
consola.warn(
|
|
87
|
+
`${themeEnvPath} dosyasında geçersiz JSON formatı. Bu dosya silinecek ve yeniden yetkilendirmeniz gerekecek.`,
|
|
88
|
+
)
|
|
89
|
+
fs.unlinkSync(themeEnvPath)
|
|
90
|
+
consola.info('Geçersiz ortam dosyası silindi. Başlatma işlemine devam ediliyor...')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (envData.bearer) {
|
|
94
|
+
const bearerTokenValid = await this.tsoft.isTokenValid(this.themeName)
|
|
95
|
+
|
|
96
|
+
if (bearerTokenValid) {
|
|
97
|
+
consola.info(
|
|
98
|
+
'Geçerli bir taşıyıcı jetonuna sahip mevcut ortam dosyası bulundu. Mevcut yapılandırma ile devam ediliyor...',
|
|
99
|
+
)
|
|
100
|
+
return true
|
|
101
|
+
} else {
|
|
102
|
+
consola.warn('Mevcut taşıyıcı jetonu geçersiz. Yeniden yetkilendirmeniz gerekecek.')
|
|
103
|
+
fs.unlinkSync(themeEnvPath)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private async changedThemeTxt() {
|
|
112
|
+
const themeTXT = getFolderFromCWD(this.tsoft.site, this.themeName, themeFileNames.themeTxt)
|
|
113
|
+
const themeFile = getFolderFromCWD(this.tsoft.site, this.themeName)
|
|
114
|
+
const result = fs.readFileSync(themeTXT).toString()
|
|
115
|
+
saveThemeData(themeFile, JSON.parse(result))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private async startWatching(themePath: string): Promise<void> {
|
|
119
|
+
this.watcher = chokidar.watch(themePath, {
|
|
120
|
+
ignored: /(^|[\/\\])(\.theme|(.*?)\.env)$/, // ignore .theme ve .env files
|
|
121
|
+
persistent: true,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
this.watcher
|
|
125
|
+
.on('change', async (filePath: string) => {
|
|
126
|
+
if (filePath.includes(themeFileNames.themeTxt)) {
|
|
127
|
+
await this.changedThemeTxt()
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
consola.info(`Dosya değiştirildi ${filePath}`)
|
|
132
|
+
await this.handleFileChange(filePath)
|
|
133
|
+
})
|
|
134
|
+
.on('unlink', async (filePath: string) => {
|
|
135
|
+
consola.success(`Dosya silindi ${filePath}`)
|
|
136
|
+
await this.handleFileDeletion(filePath)
|
|
137
|
+
})
|
|
138
|
+
.on('unlinkDir', async (dirPath: string) => {
|
|
139
|
+
consola.success(`Dizin silindi ${dirPath}`)
|
|
140
|
+
await this.handleDirectoryDeletion(dirPath)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
consola.start(`${themePath} dizinindeki dosya değişiklikleri izleniyor`)
|
|
144
|
+
|
|
145
|
+
const developmentURL = this.usageScreen()
|
|
146
|
+
|
|
147
|
+
process.stdin.setRawMode(true)
|
|
148
|
+
process.stdin.resume()
|
|
149
|
+
process.stdin.on('data', async (key) => {
|
|
150
|
+
await this.handleUserInput(key, developmentURL)
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private usageScreen(): string {
|
|
155
|
+
clearScreen()
|
|
156
|
+
|
|
157
|
+
consola.log(
|
|
158
|
+
'\n' +
|
|
159
|
+
'********************************************************\n' +
|
|
160
|
+
'* *\n' +
|
|
161
|
+
'* Dosya izlemeyi sonlandırmak için "q" tuşuna basın, *\n' +
|
|
162
|
+
'* Temayı güncellemek için "u" tuşuna basın, *\n' +
|
|
163
|
+
'* Temayı kaydetmek için "s" tuşuna basın, *\n' +
|
|
164
|
+
'* .theme dosyasını düzenlemek için "e" tuşuna basın, *\n' +
|
|
165
|
+
'* Tema dizinini açmak için "o" tuşuna basın. *\n' +
|
|
166
|
+
'* URL\'yi tarayıcıda açmak için "p" tuşuna basın. *\n' +
|
|
167
|
+
'* *\n' +
|
|
168
|
+
'********************************************************\n',
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const { clientId, clientSecret } = this.tsoft.themeManager.getEnvironment(this.themeName)
|
|
172
|
+
const url = 'https://' + this.tsoft.site + '?tsoft-cli=' + clientId + '&tsoft-cli-secret=' + clientSecret
|
|
173
|
+
consola.info('İzlenen tema: ' + this.themeName)
|
|
174
|
+
consola.info('Önizleme: \n')
|
|
175
|
+
consola.info(url + '\n')
|
|
176
|
+
|
|
177
|
+
return url
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private async closeProcess(): Promise<void> {
|
|
181
|
+
consola.log('\n')
|
|
182
|
+
consola.info('Programdan çıkılıyor. Hoşça kalın!')
|
|
183
|
+
await this.watcher.close()
|
|
184
|
+
process.exit(0)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private async handleUserInput(input: Buffer, url: string): Promise<void> {
|
|
188
|
+
//CTRL+C
|
|
189
|
+
if (input[0] == 3) {
|
|
190
|
+
await this.closeProcess()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
switch (input.toString().trim()) {
|
|
194
|
+
case 'p':
|
|
195
|
+
consola.start('URL tarayıcıda açılıyor...')
|
|
196
|
+
await openBrowser(url)
|
|
197
|
+
break
|
|
198
|
+
|
|
199
|
+
case 'u':
|
|
200
|
+
consola.start('Tema güncelleniyor...')
|
|
201
|
+
try {
|
|
202
|
+
const result = await this.updateTheme()
|
|
203
|
+
consola.success(result)
|
|
204
|
+
} catch (error) {
|
|
205
|
+
consola.error(`Tema güncelleme hatası:`, error)
|
|
206
|
+
}
|
|
207
|
+
break
|
|
208
|
+
|
|
209
|
+
case 's':
|
|
210
|
+
consola.start('Tüm dosyalar ilerleme ile yüklenmeye başlanıyor...')
|
|
211
|
+
try {
|
|
212
|
+
const result = await this.showProgress()
|
|
213
|
+
consola.success(result)
|
|
214
|
+
} catch (error) {
|
|
215
|
+
consola.error(`İlerleme gösterim hatası:`, error)
|
|
216
|
+
}
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
case 'e':
|
|
220
|
+
consola.start(`${themeFileNames.theme} dosyası düzenleniyor...`)
|
|
221
|
+
try {
|
|
222
|
+
await this.editThemeFile()
|
|
223
|
+
} catch (error) {
|
|
224
|
+
consola.error(`${themeFileNames.theme} dosyası düzenleme hatası:`, error)
|
|
225
|
+
}
|
|
226
|
+
break
|
|
227
|
+
|
|
228
|
+
case 'o':
|
|
229
|
+
consola.start('Tema dizini açılıyor...')
|
|
230
|
+
exec(`${getOpenFilePlatformCode()} ${getFolderFromCWD(this.tsoft.site, this.themeName)}`, (err) => {
|
|
231
|
+
if (err) {
|
|
232
|
+
consola.error('Tema dizini açılamadı:', err)
|
|
233
|
+
} else {
|
|
234
|
+
consola.success('Tema dizini başarıyla açıldı.')
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
break
|
|
238
|
+
|
|
239
|
+
case 'q':
|
|
240
|
+
await this.closeProcess()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private async handleFileChange(filePath: string): Promise<void> {
|
|
245
|
+
const { dirname, env } = this.setFileDirectoryOptions(filePath)
|
|
246
|
+
const content = fs.readFileSync(filePath).toString()
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
await this.tsoft.saveFile(this.themeName, dirname, content, env.bearer)
|
|
250
|
+
consola.success(`Dosya başarıyla yüklendi ${dirname}`)
|
|
251
|
+
} catch (error) {
|
|
252
|
+
consola.error(`Dosya yükleme hatası ${dirname}:`, error)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private async handleFileDeletion(filePath: string): Promise<void> {
|
|
257
|
+
const { dirname, env } = this.setFileDirectoryOptions(filePath)
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
await this.tsoft.deleteFile(this.themeName, dirname, env.bearer)
|
|
261
|
+
consola.success(`Dosya başarıyla silindi ${dirname}`)
|
|
262
|
+
} catch (error) {
|
|
263
|
+
consola.error(`Dosya silme hatası ${dirname}:`, error)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private async handleDirectoryDeletion(dirPath: string): Promise<void> {
|
|
268
|
+
const { dirname, env } = this.setFileDirectoryOptions(dirPath)
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
await this.tsoft.deleteDirectory(this.themeName, dirname, env.bearer)
|
|
272
|
+
consola.success(`Dizin başarıyla silindi ${dirname}`)
|
|
273
|
+
} catch (error) {
|
|
274
|
+
consola.error(`Dizin silme hatası ${dirname}:`, error)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private setFileDirectoryOptions(dir: string): { dirname: string; env: TThemeEnviroments } {
|
|
279
|
+
return {
|
|
280
|
+
dirname: setPathForWebhookOperations(dir.replace(`${getFolderFromCWD(this.tsoft.site, this.themeName)}`, '')),
|
|
281
|
+
env: JSON.parse(
|
|
282
|
+
fs.readFileSync(
|
|
283
|
+
getFolderFromCWD(this.tsoft.site, this.themeName, `${this.themeName}${themeFileNames.env}`),
|
|
284
|
+
'utf-8',
|
|
285
|
+
),
|
|
286
|
+
) as TThemeEnviroments,
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private async updateTheme(): Promise<string> {
|
|
291
|
+
const directoryName = path.basename(getFolderFromCWD(this.tsoft.site, this.themeName))
|
|
292
|
+
const response = await this.tsoft.updateTheme(directoryName)
|
|
293
|
+
|
|
294
|
+
if (response.status == true) {
|
|
295
|
+
return 'Successfully updated'
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
throw new Error('Update failed')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private async editThemeFile(): Promise<void> {
|
|
302
|
+
const themePath = getFolderFromCWD(this.tsoft.themeManager.site, this.themeName, themeFileNames.themeTxt)
|
|
303
|
+
if (!fs.existsSync(themePath)) {
|
|
304
|
+
consola.error(`${themeFileNames.themeTxt} file not found.`)
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
consola.start(`Opening ${themeFileNames.themeTxt} file: ${themePath}`)
|
|
309
|
+
|
|
310
|
+
exec(`${getOpenFilePlatformCode()} ${themePath}`, (err) => {
|
|
311
|
+
if (err) {
|
|
312
|
+
consola.error(`Failed to open ${themeFileNames.themeTxt} file:`, err)
|
|
313
|
+
} else {
|
|
314
|
+
consola.success(`${themeFileNames.themeTxt} file opened successfully.`)
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
public async start(themeName: string, recentlyCreated: boolean = false): Promise<void> {
|
|
320
|
+
this.themeName = themeName
|
|
321
|
+
await displayAsciiArt()
|
|
322
|
+
consola.start('Geliştirme ortamı aşağıdaki ayrıntılarla başlatılıyor:')
|
|
323
|
+
consola.info(`Site Adı: ${this.tsoft.site}`)
|
|
324
|
+
consola.info(`Tema Adı: ${this.themeName}`)
|
|
325
|
+
const themePath = getFolderFromCWD(this.tsoft.site, this.themeName)
|
|
326
|
+
const themeEnvPath = getFolderFromCWD(this.tsoft.site, this.themeName, `${this.themeName}${themeFileNames.env}`)
|
|
327
|
+
|
|
328
|
+
const existingEnvValid = await this.handleExistingEnvironment(themeEnvPath)
|
|
329
|
+
if (existingEnvValid) {
|
|
330
|
+
await this.startWatching(themePath)
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const initializedAndAuthorized = await this.initializeAndAuthorize()
|
|
335
|
+
if (initializedAndAuthorized) {
|
|
336
|
+
if (recentlyCreated) {
|
|
337
|
+
const defaultZipFile = getFolderFromCWD(this.tsoft.site, this.themeName, themeFileNames.themeZip)
|
|
338
|
+
|
|
339
|
+
consola.start('Default tema dosyaları indirme işlemi başlatılıyor, lütfen bekleyin')
|
|
340
|
+
await this.tsoft.fileManager.downloadThemeZip(
|
|
341
|
+
defaultZipFile,
|
|
342
|
+
this.tsoft.clientCredentials.bearer,
|
|
343
|
+
themeTypes.default,
|
|
344
|
+
)
|
|
345
|
+
consola.success('Default tema dosyaları indirildi.')
|
|
346
|
+
|
|
347
|
+
consola.start('Zip dosyası çıkarma işlemi başlatılıyor.')
|
|
348
|
+
const unzipResult = await unZIP(defaultZipFile, themePath)
|
|
349
|
+
if (!unzipResult) {
|
|
350
|
+
consola.error('Zip dosyası çıkarılamadı')
|
|
351
|
+
throw new Error()
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
consola.success('Zip dosyası çıkarma işlemi tamamlandı.')
|
|
355
|
+
|
|
356
|
+
//delete zip
|
|
357
|
+
fs.unlinkSync(defaultZipFile)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
await this.startWatching(themePath)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { unZIP, consola } from '@tsoft-cli/shared'
|
|
4
|
+
import { input } from '@inquirer/prompts'
|
|
5
|
+
import { CreateTheme } from './CreateTheme.js'
|
|
6
|
+
import { StartTheme } from './StartTheme.js'
|
|
7
|
+
import { Tsoft } from './Tsoft.js'
|
|
8
|
+
import { clearScreen } from '../utils/screen.js'
|
|
9
|
+
import { themeFileNames } from '../enums.js'
|
|
10
|
+
import { saveThemeTxt, slugify } from '../utils/utils.js'
|
|
11
|
+
import type { TClientCredentialsRecoveryData, TThemeFile } from './types.js'
|
|
12
|
+
|
|
13
|
+
export class SyncTheme {
|
|
14
|
+
private tsoft: Tsoft
|
|
15
|
+
private createTheme: CreateTheme
|
|
16
|
+
|
|
17
|
+
constructor(TS: Tsoft, CT: CreateTheme) {
|
|
18
|
+
this.tsoft = TS
|
|
19
|
+
this.createTheme = CT
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async start() {
|
|
23
|
+
let recoveryCode: string
|
|
24
|
+
let recoveryData: TClientCredentialsRecoveryData
|
|
25
|
+
while (!recoveryData) {
|
|
26
|
+
recoveryCode = await input({
|
|
27
|
+
message: 'Lütfen kurtarma anahtarını girin:',
|
|
28
|
+
required: true,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
if (!recoveryData) {
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
recoveryData = await this.tsoft.startRecovery(recoveryCode)
|
|
37
|
+
} catch (error) {
|
|
38
|
+
consola.error(`Hata: ${error.message}`)
|
|
39
|
+
if (error.message.includes('Çok fazla deneme yapıldı')) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!recoveryData) {
|
|
47
|
+
consola.error('Kurtarma işlemi başarısız oldu. Lütfen geçerli bir kurtarma anahtarı girin.')
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const { secret, public_key } = recoveryData
|
|
52
|
+
|
|
53
|
+
this.tsoft.clientCredentials.setClientSecret(secret).setClientId(public_key)
|
|
54
|
+
const authorizeResponse = await this.tsoft.clientCredentials.authorize()
|
|
55
|
+
if (!authorizeResponse) {
|
|
56
|
+
consola.error('Authorize işlemi başarısız oldu.')
|
|
57
|
+
throw new Error('Authorize işlemi başarısız oldu.')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const themeInformation = authorizeResponse.data.tokenable
|
|
61
|
+
|
|
62
|
+
if (!themeInformation) {
|
|
63
|
+
consola.error('Tokenable bilgisi alınamadı.')
|
|
64
|
+
throw new Error('Tokenable bilgisi alınamadı.')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const themeName = themeInformation.name
|
|
68
|
+
const themeNameSlug = slugify(themeName)
|
|
69
|
+
const themePath = await this.createTheme.createThemeDirectory(this.tsoft.site, themeNameSlug)
|
|
70
|
+
|
|
71
|
+
const themeData: TThemeFile = {
|
|
72
|
+
site: this.tsoft.site,
|
|
73
|
+
name: themeName,
|
|
74
|
+
author: themeInformation.author,
|
|
75
|
+
version: themeInformation.version,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
saveThemeTxt(themePath, themeData)
|
|
79
|
+
this.tsoft.themeManager.setTheme(themeNameSlug)
|
|
80
|
+
clearScreen()
|
|
81
|
+
|
|
82
|
+
const zipFilePath = path.join(themePath, themeFileNames.themeZip)
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
consola.start('Zip dosyası indiriliyor...')
|
|
86
|
+
await this.tsoft.fileManager.downloadThemeZip(zipFilePath, this.tsoft.clientCredentials.bearer)
|
|
87
|
+
consola.success('İndirme tamamlandı.')
|
|
88
|
+
|
|
89
|
+
consola.start('Dosyalar çıkarılıyor...')
|
|
90
|
+
const unzipResult = await unZIP(zipFilePath, themePath)
|
|
91
|
+
if (!unzipResult) {
|
|
92
|
+
throw 'Zip dosyası çıkarılamadı'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
consola.success('Çıkarma işlemi tamamlandı.')
|
|
96
|
+
fs.unlinkSync(zipFilePath)
|
|
97
|
+
} catch (error) {
|
|
98
|
+
consola.error(`Zip dosyası ile ilgili bir hata oluştu: ${error.message}`)
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.tsoft.buildEnvironment()
|
|
103
|
+
|
|
104
|
+
const startTheme = new StartTheme(this.tsoft)
|
|
105
|
+
// Geliştirme ortamını başlat
|
|
106
|
+
await startTheme.start(themeNameSlug)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import http from 'node:http'
|
|
3
|
+
import https from 'node:https'
|
|
4
|
+
import { consola } from '@tsoft-cli/shared'
|
|
5
|
+
import { getFolderFromCWD, slugify } from '../utils/utils.js'
|
|
6
|
+
import type { TClientCredentialsGetEnvironment, TThemeFile } from './types.js'
|
|
7
|
+
import type { TThemeEnviroments } from '../types.js'
|
|
8
|
+
import { themeFileNames } from '../enums.js'
|
|
9
|
+
|
|
10
|
+
export class ThemeManager {
|
|
11
|
+
public site: string
|
|
12
|
+
public theme: string
|
|
13
|
+
public registered: boolean
|
|
14
|
+
|
|
15
|
+
constructor(site: string) {
|
|
16
|
+
this.site = site
|
|
17
|
+
this.theme = null
|
|
18
|
+
this.registered = false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async initializeTheme(theme: string, reInit = false) {
|
|
22
|
+
this.theme = theme
|
|
23
|
+
const themePath = getFolderFromCWD(this.site, theme)
|
|
24
|
+
const themeEnvPath = getFolderFromCWD(this.site, theme, `${theme}${themeFileNames.env}`)
|
|
25
|
+
|
|
26
|
+
if (reInit) {
|
|
27
|
+
// await fs.rm(themePath, {recursive: true, force: true}, (err, file) => {});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(getFolderFromCWD(this.site))) {
|
|
31
|
+
fs.mkdirSync(getFolderFromCWD(this.site))
|
|
32
|
+
}
|
|
33
|
+
if (!fs.existsSync(themePath)) {
|
|
34
|
+
fs.mkdirSync(themePath)
|
|
35
|
+
}
|
|
36
|
+
if (reInit || !fs.existsSync(themeEnvPath)) {
|
|
37
|
+
fs.writeFileSync(themeEnvPath, '')
|
|
38
|
+
this.registered = false
|
|
39
|
+
} else {
|
|
40
|
+
const data = fs.readFileSync(themeEnvPath)
|
|
41
|
+
try {
|
|
42
|
+
const environment = JSON.parse(data.toString()) as TThemeEnviroments
|
|
43
|
+
this.registered = Boolean(environment.bearer)
|
|
44
|
+
} catch (e) {
|
|
45
|
+
this.registered = false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const isValid = await this.checkSiteValidity()
|
|
51
|
+
if (!isValid) {
|
|
52
|
+
consola.error('Geçersiz site: Siteye ulaşılamıyor.')
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
consola.error('Geçersiz site: Siteye ulaşılamıyor.', error.message)
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public setTheme(theme: string) {
|
|
64
|
+
this.theme = slugify(theme)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public isRegistered() {
|
|
68
|
+
return this.registered
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public buildEnvironment(environment: TClientCredentialsGetEnvironment) {
|
|
72
|
+
const envPath = getFolderFromCWD(this.site, this.theme, `${this.theme}${themeFileNames.env}`)
|
|
73
|
+
fs.writeFileSync(envPath, JSON.stringify(environment))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public getEnvironment(theme: string): TThemeEnviroments {
|
|
77
|
+
const themeEnvPath = getFolderFromCWD(this.site, slugify(theme), `${slugify(theme)}${themeFileNames.env}`)
|
|
78
|
+
return JSON.parse(fs.readFileSync(themeEnvPath).toString())
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public getThemeInfo(directoryName: string): TThemeFile {
|
|
82
|
+
const themePath = getFolderFromCWD(this.site, slugify(directoryName), themeFileNames.themeTxt)
|
|
83
|
+
|
|
84
|
+
if (fs.existsSync(themePath)) {
|
|
85
|
+
const themeInfo = fs.readFileSync(themePath, 'utf-8')
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(themeInfo)
|
|
88
|
+
} catch (error) {
|
|
89
|
+
consola.error(`${themeFileNames.themeTxt} dosyasını ayrıştırma hatası:`, error)
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
consola.error(`${themeFileNames.themeTxt} dosyası bulunamadı:`, themePath)
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public checkSiteValidity(): Promise<boolean> {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
const url = new URL(`https://${this.site}/Y/R`)
|
|
101
|
+
const request = url.protocol == 'https:' ? https : http
|
|
102
|
+
|
|
103
|
+
const req = request.get(url, (res) => {
|
|
104
|
+
if (res.statusCode === 200) {
|
|
105
|
+
return resolve(true)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return resolve(false)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
req.on('error', (err) => {
|
|
112
|
+
return reject(err)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
req.end()
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|