@wwkit/opm 1.0.2 → 1.0.4
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 +194 -11
- package/bin/index.js +23 -0
- package/docs/managers/git.md +103 -0
- package/docs/managers/proc.md +77 -0
- package/docs/managers/uv.md +9 -3
- package/docs/opm.html +154 -11
- package/docs/packages/opencode.md +46 -15
- package/docs/test/linux-environment-test-report.md +147 -0
- package/package.json +3 -2
- package/src/cli/groups/docker.js +10 -5
- package/src/cli/groups/homebrew.js +9 -5
- package/src/cli/groups/nvm.js +6 -2
- package/src/cli/groups/ping.js +461 -0
- package/src/cli/groups/proc.js +232 -0
- package/src/cli/groups/uv.js +9 -8
- package/src/cli/groups/version-manager.js +34 -8
- package/src/cli/index.js +10 -0
- package/src/config.json5 +84 -7
- package/src/harnesses/base.js +34 -0
- package/src/harnesses/opencode/index.js +56 -0
- package/src/index.js +6 -0
- package/src/managers/brew.js +6 -6
- package/src/managers/docker.js +10 -7
- package/src/managers/node.js +8 -2
- package/src/managers/proc.js +424 -0
- package/src/managers/python.js +7 -4
- package/src/presets.js +3 -0
- package/src/tools/ftp/index.js +151 -16
- package/src/tools/git/config.js +374 -0
- package/src/tools/git/index.js +378 -0
- package/src/tools/share/.htaccess +20 -0
- package/src/tools/share/client.js +172 -0
- package/src/tools/share/index.js +735 -0
- package/src/tools/share/server.js +430 -0
- package/src/tools/share/server.php +452 -0
- package/src/tools/webwork/index.js +558 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opm git 命令组 — Git 配置管理
|
|
3
|
+
*
|
|
4
|
+
* 命令:init / config / user / log / export / import / help
|
|
5
|
+
* 聚焦冷门或多步骤操作,log 支持指定 workdir 免切换目录查看提交历史。
|
|
6
|
+
* export 保存两个 commit 之间的差异到文件,默认从 mark 到 HEAD。
|
|
7
|
+
* mark 用 lightweight git tag 标记基准 commit,对用户透明。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
import { execSync } from 'node:child_process'
|
|
13
|
+
import { parseFlags } from '../../cli/helpers/args.js'
|
|
14
|
+
import { output } from '../../formatter.js'
|
|
15
|
+
import {
|
|
16
|
+
listConfig, getConfigValue, setConfigValue, unsetConfigValue,
|
|
17
|
+
getUser, setUser,
|
|
18
|
+
initGitConfig, saveDiff, importDiff,
|
|
19
|
+
getMark, setMark, unsetMark,
|
|
20
|
+
getGitVersion, installGit, uninstallGit, upgradeGit,
|
|
21
|
+
} from './config.js'
|
|
22
|
+
|
|
23
|
+
const ACTIONS = {
|
|
24
|
+
version: { desc: 'Show installed git version' },
|
|
25
|
+
install: { desc: 'Install git via system package manager (dnf/yum/apt/apk)' },
|
|
26
|
+
uninstall: { desc: 'Uninstall git via system package manager' },
|
|
27
|
+
upgrade: { desc: 'Upgrade git via system package manager' },
|
|
28
|
+
init: { desc: 'Initialize git global config from opm config (user/ssl/pull.rebase)' },
|
|
29
|
+
config: { desc: 'View/set/unset git config (with --scope global|local|system)' },
|
|
30
|
+
user: { desc: 'View/set user.name and user.email' },
|
|
31
|
+
log: { desc: 'Show commit history (with -d/--dir for workdir, -n for count)' },
|
|
32
|
+
export: { desc: 'Export diff to file (default: mark -> HEAD, or -f <n> override)' },
|
|
33
|
+
import: { desc: 'Import diff file into working tree (git apply --reject)' },
|
|
34
|
+
help: { desc: 'Show this help' },
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class GitGroup {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.name = 'git'
|
|
40
|
+
this.desc = 'Git config management (init/config/user/log/export)'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async run(argv) {
|
|
44
|
+
const [action, ...rest] = argv
|
|
45
|
+
|
|
46
|
+
if (!action || action === '-h' || action === '--help' || action === 'help') {
|
|
47
|
+
this.printHelp()
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const parsed = parseFlags(rest)
|
|
52
|
+
|
|
53
|
+
switch (action) {
|
|
54
|
+
case 'version':
|
|
55
|
+
return this._version()
|
|
56
|
+
case 'install':
|
|
57
|
+
return this._install(parsed)
|
|
58
|
+
case 'uninstall':
|
|
59
|
+
return this._uninstall()
|
|
60
|
+
case 'upgrade':
|
|
61
|
+
return this._upgrade(parsed)
|
|
62
|
+
case 'init':
|
|
63
|
+
return this._init(parsed)
|
|
64
|
+
case 'config':
|
|
65
|
+
return this._config(parsed)
|
|
66
|
+
case 'user':
|
|
67
|
+
return this._user(parsed)
|
|
68
|
+
case 'log':
|
|
69
|
+
return this._log(parsed)
|
|
70
|
+
case 'export':
|
|
71
|
+
return this._export(parsed)
|
|
72
|
+
case 'import':
|
|
73
|
+
return this._import(parsed)
|
|
74
|
+
default:
|
|
75
|
+
console.error(`Unknown action: ${action}`)
|
|
76
|
+
this.printHelp()
|
|
77
|
+
process.exit(1)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_resolveProxy(parsed) {
|
|
82
|
+
let proxy = parsed.flags.p || parsed.flags.proxy || ''
|
|
83
|
+
if (proxy === 'true') proxy = ''
|
|
84
|
+
return proxy
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_version() {
|
|
88
|
+
const result = getGitVersion()
|
|
89
|
+
output(result)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
_install(parsed) {
|
|
93
|
+
const proxy = this._resolveProxy(parsed)
|
|
94
|
+
const result = installGit(proxy)
|
|
95
|
+
output(result)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
_uninstall() {
|
|
99
|
+
const result = uninstallGit()
|
|
100
|
+
output(result)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_upgrade(parsed) {
|
|
104
|
+
const proxy = this._resolveProxy(parsed)
|
|
105
|
+
const result = upgradeGit(proxy)
|
|
106
|
+
output(result)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_getScope(parsed) {
|
|
110
|
+
return parsed.flags.scope || 'global'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async _init(parsed) {
|
|
114
|
+
const force = parsed.flags.f === 'true' || parsed.flags.force === 'true'
|
|
115
|
+
const result = await initGitConfig(force)
|
|
116
|
+
output(result)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async _config(parsed) {
|
|
120
|
+
const scope = this._getScope(parsed)
|
|
121
|
+
const unsetKey = parsed.flags.unset || ''
|
|
122
|
+
|
|
123
|
+
if (unsetKey) {
|
|
124
|
+
const ok = unsetConfigValue(unsetKey, scope)
|
|
125
|
+
output({ scope, key: unsetKey, unset: ok })
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const [key, value] = parsed.positional
|
|
130
|
+
|
|
131
|
+
if (key && value) {
|
|
132
|
+
const ok = setConfigValue(key, value, scope)
|
|
133
|
+
output({ scope, key, value, set: ok })
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (key) {
|
|
138
|
+
const val = getConfigValue(key, scope)
|
|
139
|
+
output({ scope, key, value: val })
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const result = listConfig(scope)
|
|
144
|
+
output({ scope, ...result })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async _user(parsed) {
|
|
148
|
+
const scope = this._getScope(parsed)
|
|
149
|
+
const name = parsed.flags.n || parsed.flags.name || ''
|
|
150
|
+
const email = parsed.flags.e || parsed.flags.email || ''
|
|
151
|
+
|
|
152
|
+
if (name || email) {
|
|
153
|
+
const result = setUser(name, email, scope)
|
|
154
|
+
output({ scope, ...result })
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const result = getUser(scope)
|
|
159
|
+
output({ scope, ...result })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_log(parsed) {
|
|
163
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
164
|
+
const resolvedDir = path.resolve(workDir)
|
|
165
|
+
if (!this._getGitDir(resolvedDir)) {
|
|
166
|
+
console.error(`Not a git repository: ${resolvedDir}`)
|
|
167
|
+
process.exit(1)
|
|
168
|
+
}
|
|
169
|
+
const n = parseInt(parsed.flags.n || parsed.flags.count || '10', 10) || 10
|
|
170
|
+
const opts = { encoding: 'utf8', timeout: 5000, cwd: resolvedDir, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
171
|
+
const raw = execSync(`git log --oneline -${n}`, opts).trim()
|
|
172
|
+
if (!raw) {
|
|
173
|
+
output({ commits: [], message: 'No commits yet' })
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
const commits = raw.split('\n').map((line) => {
|
|
177
|
+
const idx = line.indexOf(' ')
|
|
178
|
+
return { hash: line.slice(0, idx), subject: line.slice(idx + 1) }
|
|
179
|
+
})
|
|
180
|
+
output({ dir: resolvedDir, count: commits.length, commits })
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
_printRecentLog(workDir, count = 5) {
|
|
184
|
+
const resolvedDir = path.resolve(workDir)
|
|
185
|
+
if (!this._getGitDir(resolvedDir)) return
|
|
186
|
+
const opts = { encoding: 'utf8', timeout: 5000, cwd: resolvedDir, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
187
|
+
try {
|
|
188
|
+
const raw = execSync(`git log --oneline -${count}`, opts).trim()
|
|
189
|
+
if (raw) {
|
|
190
|
+
console.error(`\nRecent ${count} commits:`)
|
|
191
|
+
for (const line of raw.split('\n')) {
|
|
192
|
+
console.error(` ${line}`)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async _export(parsed) {
|
|
199
|
+
const [sub] = parsed.positional
|
|
200
|
+
|
|
201
|
+
if (sub === 'mark') {
|
|
202
|
+
return this._exportMark(parsed)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return this._exportDiff(parsed)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_exportMark(parsed) {
|
|
209
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
210
|
+
const ref = parsed.positional[1]
|
|
211
|
+
|
|
212
|
+
if (parsed.flags.unset === 'true') {
|
|
213
|
+
const ok = unsetMark(workDir)
|
|
214
|
+
output({ mark: 'removed', ok })
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!ref) {
|
|
219
|
+
const mark = getMark(workDir)
|
|
220
|
+
if (!mark) {
|
|
221
|
+
output({ mark: 'not-set' })
|
|
222
|
+
console.error('\nTips: mark not set, set a diff base first:')
|
|
223
|
+
console.error(' opm git export mark HEAD # mark current HEAD as diff base')
|
|
224
|
+
console.error(' opm git export mark <commit> # mark a commit as diff base')
|
|
225
|
+
console.error(' opm git log -d <dir> -n 10 # view commit history without cd')
|
|
226
|
+
this._printRecentLog(workDir)
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
output({ mark: 'set', ...mark })
|
|
230
|
+
console.error('\nTips:')
|
|
231
|
+
console.error(' opm git log -d <dir> -n 10 # view commit history without cd')
|
|
232
|
+
console.error(' opm git export mark <commit> # change diff base to another commit')
|
|
233
|
+
console.error(' opm git export mark --unset # remove current mark')
|
|
234
|
+
console.error(' opm git export # export diff (mark -> HEAD)')
|
|
235
|
+
this._printRecentLog(workDir)
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const resolvedRef = ref === '0' || ref.toLowerCase() === 'head' ? 'HEAD' : ref
|
|
240
|
+
const result = setMark(workDir, resolvedRef)
|
|
241
|
+
output({ mark: 'set', ...result })
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
_exportDiff(parsed) {
|
|
245
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
246
|
+
const resolvedDir = path.resolve(workDir)
|
|
247
|
+
const gitDir = this._getGitDir(resolvedDir)
|
|
248
|
+
if (!gitDir) {
|
|
249
|
+
console.error(`Not a git repository: ${resolvedDir}`)
|
|
250
|
+
process.exit(1)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const fromRaw = parsed.flags.f || parsed.flags.from || ''
|
|
254
|
+
let fromRef
|
|
255
|
+
if (fromRaw) {
|
|
256
|
+
const n = parseInt(fromRaw, 10)
|
|
257
|
+
fromRef = isNaN(n) ? fromRaw : `HEAD~${n}`
|
|
258
|
+
} else {
|
|
259
|
+
const mark = getMark(resolvedDir)
|
|
260
|
+
if (!mark) {
|
|
261
|
+
console.error('No mark found. Set a mark first:')
|
|
262
|
+
console.error(' opm git export mark HEAD # mark current HEAD as diff base')
|
|
263
|
+
console.error(' opm git export mark <commit> # mark a commit as diff base')
|
|
264
|
+
console.error(' opm git log -d <dir> -n 10 # view commit history without cd')
|
|
265
|
+
process.exit(1)
|
|
266
|
+
}
|
|
267
|
+
fromRef = mark.commit
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
let outFile = parsed.flags.o || parsed.flags.output || ''
|
|
271
|
+
if (!outFile) {
|
|
272
|
+
const opts = { encoding: 'utf8', timeout: 5000, cwd: resolvedDir, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
273
|
+
const gitRoot = execSync('git rev-parse --show-toplevel', opts).trim()
|
|
274
|
+
const name = parsed.flags.n || parsed.flags.name || ''
|
|
275
|
+
const fileName = name ? `${name}.diff` : (() => {
|
|
276
|
+
const fromShort = execSync(`git rev-parse --short ${fromRef}`, opts).trim()
|
|
277
|
+
const toShort = execSync('git rev-parse --short HEAD', opts).trim()
|
|
278
|
+
return `changes-${fromShort}-to-${toShort}.diff`
|
|
279
|
+
})()
|
|
280
|
+
outFile = path.join(gitRoot, '.webwork', 'git', fileName)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const filePath = path.resolve(outFile)
|
|
284
|
+
const dir = path.dirname(filePath)
|
|
285
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
286
|
+
|
|
287
|
+
const result = saveDiff(resolvedDir, filePath, fromRef)
|
|
288
|
+
output(result)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async _import(parsed) {
|
|
292
|
+
const diffFile = parsed.positional[0]
|
|
293
|
+
if (!diffFile) {
|
|
294
|
+
console.error('Usage: opm git import <difffile> [-d <workdir>]')
|
|
295
|
+
process.exit(1)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
299
|
+
const resolvedDir = path.resolve(workDir)
|
|
300
|
+
const gitDir = this._getGitDir(resolvedDir)
|
|
301
|
+
if (!gitDir) {
|
|
302
|
+
console.error(`Not a git repository: ${resolvedDir}`)
|
|
303
|
+
process.exit(1)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const result = importDiff(resolvedDir, diffFile)
|
|
307
|
+
output(result)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_getGitDir(dir) {
|
|
311
|
+
try {
|
|
312
|
+
execSync('git rev-parse --git-dir', { encoding: 'utf8', timeout: 5000, cwd: dir, stdio: ['pipe', 'pipe', 'pipe'] })
|
|
313
|
+
return true
|
|
314
|
+
} catch {
|
|
315
|
+
return false
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
printHelp() {
|
|
320
|
+
const actionLines = Object.entries(ACTIONS)
|
|
321
|
+
.map(([sig, { desc }]) => ` ${sig.padEnd(12)} ${desc}`)
|
|
322
|
+
.join('\n')
|
|
323
|
+
|
|
324
|
+
console.log(`
|
|
325
|
+
Usage: opm git <action> [options]
|
|
326
|
+
|
|
327
|
+
Actions:
|
|
328
|
+
${actionLines}
|
|
329
|
+
|
|
330
|
+
Export subcommands:
|
|
331
|
+
export mark [-d dir] Show current diff base mark
|
|
332
|
+
export mark 0|HEAD [-d dir] Set diff base mark to current HEAD
|
|
333
|
+
export mark <commit> [-d dir] Set diff base mark to a commit
|
|
334
|
+
export mark --unset [-d dir] Remove the diff base mark
|
|
335
|
+
export [-d dir] Export diff (mark -> HEAD)
|
|
336
|
+
export -f <n> [-d dir] Export diff (HEAD~n -> HEAD, override mark)
|
|
337
|
+
export -f <ref> [-d dir] Export diff (<ref> -> HEAD, override mark)
|
|
338
|
+
|
|
339
|
+
Options:
|
|
340
|
+
-d, --dir <dir> Git repository directory (default: cwd)
|
|
341
|
+
-p, --proxy <url> Proxy for install/upgrade
|
|
342
|
+
--scope <global|local|system> Config scope (default: global)
|
|
343
|
+
-n, --name <name> User name (user) / diff file name (export)
|
|
344
|
+
-n, --count <n> Number of commits to show (log, default: 10)
|
|
345
|
+
-e, --email <email> User email (user)
|
|
346
|
+
-o, --output <file> Output file path (export)
|
|
347
|
+
-f, --from <n>|<ref> Override from ref (export)
|
|
348
|
+
-f, --force Force overwrite existing config (init)
|
|
349
|
+
--unset <key> Unset a config key (config)
|
|
350
|
+
-h, --help Show this help
|
|
351
|
+
|
|
352
|
+
Examples:
|
|
353
|
+
opm git version Show installed git version
|
|
354
|
+
opm git install Install git via system package manager
|
|
355
|
+
opm git upgrade Upgrade git to latest
|
|
356
|
+
opm git uninstall Uninstall git
|
|
357
|
+
opm git init Initialize global config from opm config
|
|
358
|
+
opm git config List all global config
|
|
359
|
+
opm git config pull.rebase true Set pull to use rebase
|
|
360
|
+
opm git user Show current user.name and user.email
|
|
361
|
+
opm git log Show recent 10 commits (cwd)
|
|
362
|
+
opm git log -n 5 Show recent 5 commits (cwd)
|
|
363
|
+
opm git log -d /path/to/repo Show commits in specified repo
|
|
364
|
+
opm git export mark Show current diff base mark (cwd)
|
|
365
|
+
opm git export mark 0 Set diff base to current HEAD (cwd)
|
|
366
|
+
opm git export mark HEAD Set diff base to current HEAD (cwd)
|
|
367
|
+
opm git export mark 3af967b Set diff base at commit (cwd)
|
|
368
|
+
opm git export mark --unset Remove diff base mark (cwd)
|
|
369
|
+
opm git export mark abc -d /repo Set diff base at commit in /repo
|
|
370
|
+
opm git export Export diff in current repo (mark -> HEAD)
|
|
371
|
+
opm git export -f 2 Override: HEAD~2 -> HEAD (current repo)
|
|
372
|
+
opm git export -d /path/to/repo Export diff in specified repo
|
|
373
|
+
opm git export -n feature Save as feature.diff
|
|
374
|
+
opm git import file.diff Import diff file (current repo)
|
|
375
|
+
opm git import file.diff -d /repo Import diff file into specified repo
|
|
376
|
+
`)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# opm share server — Apache URL rewrite
|
|
2
|
+
# 将所有 /share/* 请求路由到 server.php
|
|
3
|
+
|
|
4
|
+
<IfModule mod_rewrite.c>
|
|
5
|
+
RewriteEngine On
|
|
6
|
+
|
|
7
|
+
# 如果请求的是已存在的文件,直接返回
|
|
8
|
+
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
|
9
|
+
RewriteCond %{REQUEST_FILENAME} -d
|
|
10
|
+
RewriteRule ^ - [L]
|
|
11
|
+
|
|
12
|
+
# /share/* 路由到 server.php
|
|
13
|
+
RewriteRule ^share/?$ server.php [L,QSA]
|
|
14
|
+
RewriteRule ^share/(.+)$ server.php [L,QSA]
|
|
15
|
+
</IfModule>
|
|
16
|
+
|
|
17
|
+
# 保护 data 目录,禁止直接访问
|
|
18
|
+
<IfModule mod_rewrite.c>
|
|
19
|
+
RewriteRule ^data/ - [F]
|
|
20
|
+
</IfModule>
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* share HTTP 客户端 — 上传/取回/列表
|
|
3
|
+
*
|
|
4
|
+
* 通过 fetch 调用 share server API。
|
|
5
|
+
* 服务地址由 config.share.server.active 解析(key → URL,URL 原样返回)。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 解析 active server URL
|
|
10
|
+
* @param {object} shareConfig - config.share 段
|
|
11
|
+
* @returns {string} base URL(无尾斜杠)
|
|
12
|
+
*/
|
|
13
|
+
export function getShareServer(shareConfig) {
|
|
14
|
+
const server = shareConfig?.server ?? {}
|
|
15
|
+
const active = server.active
|
|
16
|
+
if (!active) return server.official || ''
|
|
17
|
+
return server[active] || active
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* ping 检测 server 可达性
|
|
22
|
+
* @param {string} baseUrl
|
|
23
|
+
* @returns {Promise<{reachable: boolean, status: number, message: string}>}
|
|
24
|
+
*/
|
|
25
|
+
export async function pingServer(baseUrl) {
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetch(`${baseUrl}/share/list?n=1`, {
|
|
28
|
+
signal: AbortSignal.timeout(5000),
|
|
29
|
+
})
|
|
30
|
+
if (res.ok) {
|
|
31
|
+
return { reachable: true, status: res.status, message: 'OK' }
|
|
32
|
+
}
|
|
33
|
+
if (res.status === 404) {
|
|
34
|
+
return { reachable: true, status: res.status, message: 'OK (404 on list endpoint is acceptable)' }
|
|
35
|
+
}
|
|
36
|
+
return { reachable: false, status: res.status, message: `HTTP ${res.status}` }
|
|
37
|
+
} catch (err) {
|
|
38
|
+
return { reachable: false, status: 0, message: err.message }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 上传分享内容
|
|
44
|
+
* @param {string} baseUrl
|
|
45
|
+
* @param {{title?: string, content: string, password?: string, id?: string, type?: string, filename?: string}} data
|
|
46
|
+
* @returns {Promise<{id: string, url: string, htmlUrl: string}>}
|
|
47
|
+
*/
|
|
48
|
+
export async function uploadShare(baseUrl, data) {
|
|
49
|
+
const res = await fetch(`${baseUrl}/share`, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: { 'Content-Type': 'application/json' },
|
|
52
|
+
body: JSON.stringify(data),
|
|
53
|
+
signal: AbortSignal.timeout(30000),
|
|
54
|
+
})
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const body = await res.text()
|
|
57
|
+
throw new Error(`Upload failed: HTTP ${res.status} ${body}`)
|
|
58
|
+
}
|
|
59
|
+
return res.json()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 取回分享内容
|
|
64
|
+
* @param {string} baseUrl
|
|
65
|
+
* @param {string} id
|
|
66
|
+
* @param {string} [password]
|
|
67
|
+
* @returns {Promise<{id: string, title: string, content: string, created: string}>}
|
|
68
|
+
*/
|
|
69
|
+
export async function getShare(baseUrl, id, password) {
|
|
70
|
+
const url = password
|
|
71
|
+
? `${baseUrl}/share/${id}/${password}`
|
|
72
|
+
: `${baseUrl}/share/${id}`
|
|
73
|
+
const res = await fetch(url, {
|
|
74
|
+
headers: { 'Accept': 'application/json' },
|
|
75
|
+
signal: AbortSignal.timeout(10000),
|
|
76
|
+
})
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
const body = await res.text()
|
|
79
|
+
throw new Error(`Get failed: HTTP ${res.status} ${body}`)
|
|
80
|
+
}
|
|
81
|
+
return res.json()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 取回最近一条分享内容
|
|
86
|
+
* @param {string} baseUrl
|
|
87
|
+
* @param {string} [password]
|
|
88
|
+
* @returns {Promise<{id: string, title: string, content: string, created: string}>}
|
|
89
|
+
*/
|
|
90
|
+
export async function getLatestShare(baseUrl, password) {
|
|
91
|
+
const url = password
|
|
92
|
+
? `${baseUrl}/share/latest/${password}`
|
|
93
|
+
: `${baseUrl}/share/latest`
|
|
94
|
+
const res = await fetch(url, {
|
|
95
|
+
headers: { 'Accept': 'application/json' },
|
|
96
|
+
signal: AbortSignal.timeout(10000),
|
|
97
|
+
})
|
|
98
|
+
if (!res.ok) {
|
|
99
|
+
const body = await res.text()
|
|
100
|
+
throw new Error(`Get latest failed: HTTP ${res.status} ${body}`)
|
|
101
|
+
}
|
|
102
|
+
return res.json()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 获取 HTML 页面 URL
|
|
107
|
+
* @param {string} baseUrl
|
|
108
|
+
* @param {string} id
|
|
109
|
+
* @param {string} [password]
|
|
110
|
+
* @returns {string}
|
|
111
|
+
*/
|
|
112
|
+
export function getHtmlUrl(baseUrl, id, password) {
|
|
113
|
+
return password
|
|
114
|
+
? `${baseUrl}/share/html/${id}/${password}`
|
|
115
|
+
: `${baseUrl}/share/html/${id}`
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 列出最近分享
|
|
120
|
+
* @param {string} baseUrl
|
|
121
|
+
* @param {number} n
|
|
122
|
+
* @returns {Promise<{count: number, items: Array<{id: string, title: string, created: string}>}>}
|
|
123
|
+
*/
|
|
124
|
+
export async function listShares(baseUrl, n) {
|
|
125
|
+
const res = await fetch(`${baseUrl}/share/list?n=${n}`, {
|
|
126
|
+
signal: AbortSignal.timeout(10000),
|
|
127
|
+
})
|
|
128
|
+
if (!res.ok) {
|
|
129
|
+
const body = await res.text()
|
|
130
|
+
throw new Error(`List failed: HTTP ${res.status} ${body}`)
|
|
131
|
+
}
|
|
132
|
+
return res.json()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 删除指定分享
|
|
137
|
+
* @param {string} baseUrl
|
|
138
|
+
* @param {string} id
|
|
139
|
+
* @returns {Promise<{deleted: boolean, id: string}>}
|
|
140
|
+
*/
|
|
141
|
+
export async function deleteShare(baseUrl, id) {
|
|
142
|
+
const res = await fetch(`${baseUrl}/share/delete/${id}`, {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
signal: AbortSignal.timeout(10000),
|
|
145
|
+
})
|
|
146
|
+
if (!res.ok) {
|
|
147
|
+
const body = await res.text()
|
|
148
|
+
throw new Error(`Delete failed: HTTP ${res.status} ${body}`)
|
|
149
|
+
}
|
|
150
|
+
return res.json()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 清理过期/超额分享
|
|
155
|
+
* @param {string} baseUrl
|
|
156
|
+
* @param {{days?: number, keep?: number}} opts
|
|
157
|
+
* @returns {Promise<{deleted: number, ids: string[]}>}
|
|
158
|
+
*/
|
|
159
|
+
export async function clearShares(baseUrl, opts) {
|
|
160
|
+
const params = new URLSearchParams()
|
|
161
|
+
if (opts.days) params.set('days', String(opts.days))
|
|
162
|
+
if (opts.keep) params.set('keep', String(opts.keep))
|
|
163
|
+
const res = await fetch(`${baseUrl}/share/clear?${params}`, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
signal: AbortSignal.timeout(30000),
|
|
166
|
+
})
|
|
167
|
+
if (!res.ok) {
|
|
168
|
+
const body = await res.text()
|
|
169
|
+
throw new Error(`Clear failed: HTTP ${res.status} ${body}`)
|
|
170
|
+
}
|
|
171
|
+
return res.json()
|
|
172
|
+
}
|