@wwkit/opm 1.0.3 → 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/package.json +2 -2
- package/src/cli/index.js +4 -0
- package/src/config.json5 +18 -0
- package/src/index.js +2 -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wwkit/opm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"author": "bluesliu <langcai163@163.com>",
|
|
5
5
|
"description": "Unified CLI — package management (npm/pip/dnf/apt) + config view + opencode maintenance",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"basic-ftp": "^6.2.1",
|
|
36
36
|
"extract-zip": "^2.0.1",
|
|
37
37
|
"systeminformation": "^5.23.0",
|
|
38
|
-
"@wwkit/shared": "1.0.
|
|
38
|
+
"@wwkit/shared": "1.0.7"
|
|
39
39
|
},
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"registry": "https://registry.npmjs.org/",
|
package/src/cli/index.js
CHANGED
|
@@ -22,6 +22,8 @@ import { PingGroup } from './groups/ping.js'
|
|
|
22
22
|
import { ProcGroup } from './groups/proc.js'
|
|
23
23
|
import { FtpGroup } from '../tools/ftp/index.js'
|
|
24
24
|
import { GitGroup } from '../tools/git/index.js'
|
|
25
|
+
import { ShareGroup } from '../tools/share/index.js'
|
|
26
|
+
import { WebworkGroup } from '../tools/webwork/index.js'
|
|
25
27
|
import { HarnessGroup } from '../harnesses/base.js'
|
|
26
28
|
import { opencodeHarness } from '../harnesses/opencode/index.js'
|
|
27
29
|
import { CftGroup } from '../binaries/cft/index.js'
|
|
@@ -43,6 +45,8 @@ const GROUPS = {
|
|
|
43
45
|
docker: new DockerGroup(),
|
|
44
46
|
ftp: new FtpGroup(),
|
|
45
47
|
git: new GitGroup(),
|
|
48
|
+
share: new ShareGroup(),
|
|
49
|
+
webwork: new WebworkGroup(),
|
|
46
50
|
composer: new PackageGroup('composer'),
|
|
47
51
|
bun: new PackageGroup('bun'),
|
|
48
52
|
winget: new PackageGroup('winget'),
|
package/src/config.json5
CHANGED
|
@@ -229,4 +229,22 @@
|
|
|
229
229
|
OPENCODE_CONFIG_CONTENT: "",
|
|
230
230
|
},
|
|
231
231
|
},
|
|
232
|
+
|
|
233
|
+
// share 文本分享服务
|
|
234
|
+
// server.active 指向预设 key(official/local)或完整 URL
|
|
235
|
+
// serve 为本地服务参数(opm share serve 使用)
|
|
236
|
+
// password 为分享默认密码(opm share upload/get/open 使用,-p/--password 可覆盖)
|
|
237
|
+
share: {
|
|
238
|
+
password: "0000",
|
|
239
|
+
server: {
|
|
240
|
+
official: "http://deepbluenet.com",
|
|
241
|
+
local: "http://127.0.0.1:8787",
|
|
242
|
+
active: "local",
|
|
243
|
+
},
|
|
244
|
+
serve: {
|
|
245
|
+
host: "0.0.0.0",
|
|
246
|
+
port: 8787,
|
|
247
|
+
dataDir: "~/.config/opm/share/data",
|
|
248
|
+
},
|
|
249
|
+
},
|
|
232
250
|
}
|
package/src/index.js
CHANGED
|
@@ -23,6 +23,8 @@ export { PingGroup } from './cli/groups/ping.js'
|
|
|
23
23
|
export { ProcGroup } from './cli/groups/proc.js'
|
|
24
24
|
export { FtpGroup } from './tools/ftp/index.js'
|
|
25
25
|
export { GitGroup } from './tools/git/index.js'
|
|
26
|
+
export { ShareGroup } from './tools/share/index.js'
|
|
27
|
+
export { WebworkGroup } from './tools/webwork/index.js'
|
|
26
28
|
export { ToolManagerGroup } from './cli/groups/version-manager.js'
|
|
27
29
|
export { parseFlags } from './cli/helpers/args.js'
|
|
28
30
|
|
|
@@ -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
|
+
}
|