most-box 0.1.1 → 0.1.2
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 +21 -14
- package/electron/main.js +72 -8
- package/electron/preload.js +6 -0
- package/out/404/index.html +2 -2
- package/out/404.html +2 -2
- package/out/__next.__PAGE__.txt +6 -6
- package/out/__next._full.txt +16 -16
- package/out/__next._head.txt +3 -3
- package/out/__next._index.txt +8 -8
- package/out/__next._tree.txt +5 -5
- package/out/_next/static/chunks/0.e2avjgna_b2.js +1 -0
- package/out/_next/static/chunks/{0ho~log~~-jwp.css → 03h~nhgj0hv3p.css} +1 -1
- package/out/_next/static/chunks/07td.jq7xff84.css +1 -0
- package/out/_next/static/chunks/{0qou.u2e2dy48.css → 0adx~d-j05c9d.css} +2 -2
- package/out/_next/static/chunks/0aq.rc9woa2nz.js +1 -0
- package/out/_next/static/chunks/0etes81d_cihn.js +1 -0
- package/out/_next/static/chunks/0g_a~e050bgzg.css +1 -0
- package/out/_next/static/chunks/{0o6lrkxy4jwag.js → 0gcsdf57gcm6h.js} +1 -1
- package/out/_next/static/chunks/0gwian.hp3-92.js +1 -0
- package/out/_next/static/chunks/0hpev4am9jpmu.css +1 -0
- package/out/_next/static/chunks/0l5_.uqb-uqb8.js +1 -0
- package/out/_next/static/chunks/0mex8svsiv-2l.js +1 -0
- package/out/_next/static/chunks/0myq9gs8szydh.js +1 -0
- package/out/_next/static/chunks/{0usvo~vu7r8np.js → 0o9ce4cyf76by.js} +1 -1
- package/out/_next/static/chunks/0p0sv~fuddvgr.js +1 -0
- package/out/_next/static/chunks/{0o98f1yq..o.8.js → 0pt.5cg1t09qs.js} +1 -1
- package/out/_next/static/chunks/0q0ksgxg98xgd.js +1 -0
- package/out/_next/static/chunks/0qgx9t4jx16ua.css +1 -0
- package/out/_next/static/chunks/0ukyg~tkm~h2m.css +1 -0
- package/out/_next/static/chunks/0wtf0xsiicxx6.js +1 -0
- package/out/_next/static/chunks/0xdwau5k2augv.css +4 -0
- package/out/_next/static/chunks/12nr19.nnn6s3.js +5 -0
- package/out/_next/static/chunks/{0qub_r0x_r-e9.css → 12pep-2t-qg4n.css} +1 -1
- package/out/_next/static/chunks/14_inksek_rth.js +2 -0
- package/out/_next/static/chunks/153-sz7s.qml2.js +1 -0
- package/out/_next/static/chunks/16xls5tt_68lx.js +1 -0
- package/out/_next/static/chunks/{turbopack-0xs6mybc~5t_3.js → turbopack-0xta0kqwzkf28.js} +1 -1
- package/out/_not-found/__next._full.txt +13 -13
- package/out/_not-found/__next._head.txt +3 -3
- package/out/_not-found/__next._index.txt +8 -8
- package/out/_not-found/__next._not-found.__PAGE__.txt +4 -4
- package/out/_not-found/__next._not-found.txt +3 -3
- package/out/_not-found/__next._tree.txt +3 -3
- package/out/_not-found/index.html +2 -2
- package/out/_not-found/index.txt +13 -13
- package/out/admin/__next._full.txt +15 -15
- package/out/admin/__next._head.txt +3 -3
- package/out/admin/__next._index.txt +8 -8
- package/out/admin/__next._tree.txt +4 -4
- package/out/admin/__next.admin.__PAGE__.txt +4 -4
- package/out/admin/__next.admin.txt +4 -4
- package/out/admin/index.html +2 -2
- package/out/admin/index.txt +15 -15
- package/out/app/__next._full.txt +14 -14
- package/out/app/__next._head.txt +3 -3
- package/out/app/__next._index.txt +8 -8
- package/out/app/__next._tree.txt +3 -3
- package/out/app/__next.app.__PAGE__.txt +4 -4
- package/out/app/__next.app.txt +3 -3
- package/out/app/index.html +2 -2
- package/out/app/index.txt +14 -14
- package/out/chat/__next._full.txt +15 -15
- package/out/chat/__next._head.txt +3 -3
- package/out/chat/__next._index.txt +8 -8
- package/out/chat/__next._tree.txt +4 -4
- package/out/chat/__next.chat.__PAGE__.txt +4 -4
- package/out/chat/__next.chat.txt +4 -4
- package/out/chat/index.html +2 -2
- package/out/chat/index.txt +15 -15
- package/out/chat/join/__next._full.txt +25 -0
- package/out/chat/join/__next._head.txt +5 -0
- package/out/{changelog → chat/join}/__next._index.txt +8 -8
- package/out/chat/join/__next._tree.txt +5 -0
- package/out/chat/join/__next.chat.join.__PAGE__.txt +9 -0
- package/out/chat/join/__next.chat.join.txt +5 -0
- package/out/chat/join/__next.chat.txt +5 -0
- package/out/chat/join/index.html +15 -0
- package/out/chat/join/index.txt +25 -0
- package/out/download/__next._full.txt +21 -21
- package/out/download/__next._head.txt +3 -3
- package/out/download/__next._index.txt +8 -8
- package/out/download/__next._tree.txt +5 -5
- package/out/download/__next.download.__PAGE__.txt +9 -9
- package/out/download/__next.download.txt +3 -3
- package/out/download/index.html +2 -2
- package/out/download/index.txt +21 -21
- package/out/index.html +2 -2
- package/out/index.txt +16 -16
- package/out/note/__next._full.txt +14 -14
- package/out/note/__next._head.txt +3 -3
- package/out/note/__next._index.txt +8 -8
- package/out/note/__next._tree.txt +3 -3
- package/out/note/__next.note.__PAGE__.txt +4 -4
- package/out/note/__next.note.txt +3 -3
- package/out/note/index.html +2 -2
- package/out/note/index.txt +14 -14
- package/out/ping/__next._full.txt +16 -16
- package/out/ping/__next._head.txt +3 -3
- package/out/ping/__next._index.txt +8 -8
- package/out/ping/__next._tree.txt +5 -5
- package/out/ping/__next.ping.__PAGE__.txt +5 -5
- package/out/ping/__next.ping.txt +4 -4
- package/out/ping/index.html +2 -2
- package/out/ping/index.txt +16 -16
- package/out/web3/__next._full.txt +15 -15
- package/out/web3/__next._head.txt +3 -3
- package/out/web3/__next._index.txt +8 -8
- package/out/web3/__next._tree.txt +4 -4
- package/out/web3/__next.web3.__PAGE__.txt +4 -4
- package/out/web3/__next.web3.txt +4 -4
- package/out/web3/ed25519/__next._full.txt +13 -13
- package/out/web3/ed25519/__next._head.txt +3 -3
- package/out/web3/ed25519/__next._index.txt +8 -8
- package/out/web3/ed25519/__next._tree.txt +4 -4
- package/out/web3/ed25519/__next.web3.ed25519.__PAGE__.txt +2 -2
- package/out/web3/ed25519/__next.web3.ed25519.txt +3 -3
- package/out/web3/ed25519/__next.web3.txt +4 -4
- package/out/web3/ed25519/index.html +1 -1
- package/out/web3/ed25519/index.txt +13 -13
- package/out/web3/index.html +2 -2
- package/out/web3/index.txt +15 -15
- package/out/web3/tools/__next._full.txt +13 -13
- package/out/web3/tools/__next._head.txt +3 -3
- package/out/web3/tools/__next._index.txt +8 -8
- package/out/web3/tools/__next._tree.txt +4 -4
- package/out/web3/tools/__next.web3.tools.__PAGE__.txt +2 -2
- package/out/web3/tools/__next.web3.tools.txt +3 -3
- package/out/web3/tools/__next.web3.txt +4 -4
- package/out/web3/tools/index.html +1 -1
- package/out/web3/tools/index.txt +13 -13
- package/package.json +19 -9
- package/server/index.js +133 -1303
- package/server/src/config.js +1 -1
- package/server/src/core/channelAttachment.js +68 -0
- package/server/src/core/cid.js +2 -88
- package/server/src/core/cidTopic.js +29 -0
- package/server/src/core/mostLink.js +88 -0
- package/server/src/http/access.js +123 -0
- package/server/src/http/app.js +1095 -0
- package/server/src/http/errors.js +35 -0
- package/server/src/http/nodeLogs.js +53 -0
- package/server/src/http/nodeStatus.js +146 -0
- package/server/src/http/staticFiles.js +84 -0
- package/server/src/http/uploads.js +114 -0
- package/server/src/index.js +799 -211
- package/server/src/node/config.js +38 -6
- package/server/src/utils/api.js +287 -14
- package/server/src/utils/auth.js +63 -0
- package/server/src/utils/dateTime.js +30 -0
- package/server/src/utils/downloadMessages.js +89 -0
- package/server/src/utils/errors.js +7 -0
- package/server/src/utils/mostWallet.js +151 -0
- package/server/src/utils/mp.js +2 -26
- package/server/src/utils/noteBackup.js +2 -5
- package/server/src/utils/noteUtils.js +11 -3
- package/server/src/utils/userIdentity.js +0 -1
- package/out/_next/static/chunks/00-u5nq76f0.j.js +0 -1
- package/out/_next/static/chunks/00fm8lijienf1.js +0 -1
- package/out/_next/static/chunks/00o9ht.f2qm00.css +0 -4
- package/out/_next/static/chunks/00zi-erhjrny2.js +0 -2
- package/out/_next/static/chunks/084xf0edl9sfo.js +0 -1
- package/out/_next/static/chunks/09f1gfke9m5wg.css +0 -1
- package/out/_next/static/chunks/09xyi6fpro_d-.css +0 -1
- package/out/_next/static/chunks/0_npg_pcoywti.js +0 -5
- package/out/_next/static/chunks/0_r_mk1~6bosc.js +0 -1
- package/out/_next/static/chunks/0arm0a6adt7cc.css +0 -1
- package/out/_next/static/chunks/0c9j3eq_14vv2.css +0 -1
- package/out/_next/static/chunks/0d4bueddmcnca.js +0 -1
- package/out/_next/static/chunks/0gtwvy1z9ksa7.css +0 -1
- package/out/_next/static/chunks/0j27tcmtt4ly7.js +0 -1
- package/out/_next/static/chunks/0j3v4mq67wtnh.js +0 -1
- package/out/_next/static/chunks/0lkmf5ry.s_7w.js +0 -1
- package/out/_next/static/chunks/0p486m03-zfoi.js +0 -1
- package/out/_next/static/chunks/0r1~k82nji8sf.js +0 -1
- package/out/_next/static/chunks/0v7qp4hv-_._r.js +0 -1
- package/out/_next/static/chunks/0wuwlgcn6gxqt.js +0 -1
- package/out/_next/static/chunks/0xl5_avhu._i8.js +0 -1
- package/out/_next/static/chunks/10kvl8vj_plm-.js +0 -1
- package/out/_next/static/chunks/16m27azcs4k6w.js +0 -1
- package/out/changelog/__next._full.txt +0 -25
- package/out/changelog/__next._head.txt +0 -5
- package/out/changelog/__next._tree.txt +0 -5
- package/out/changelog/__next.changelog.__PAGE__.txt +0 -10
- package/out/changelog/__next.changelog.txt +0 -5
- package/out/changelog/index.html +0 -15
- package/out/changelog/index.txt +0 -25
- package/out/docs/__next._full.txt +0 -25
- package/out/docs/__next._head.txt +0 -5
- package/out/docs/__next._index.txt +0 -9
- package/out/docs/__next._tree.txt +0 -5
- package/out/docs/__next.docs.__PAGE__.txt +0 -10
- package/out/docs/__next.docs.txt +0 -5
- package/out/docs/getting-started/__next._full.txt +0 -25
- package/out/docs/getting-started/__next._head.txt +0 -5
- package/out/docs/getting-started/__next._index.txt +0 -9
- package/out/docs/getting-started/__next._tree.txt +0 -5
- package/out/docs/getting-started/__next.docs.getting-started.__PAGE__.txt +0 -10
- package/out/docs/getting-started/__next.docs.getting-started.txt +0 -5
- package/out/docs/getting-started/__next.docs.txt +0 -5
- package/out/docs/getting-started/index.html +0 -15
- package/out/docs/getting-started/index.txt +0 -25
- package/out/docs/index.html +0 -15
- package/out/docs/index.txt +0 -25
- package/out/note/edit/__next._full.txt +0 -24
- package/out/note/edit/__next._head.txt +0 -5
- package/out/note/edit/__next._index.txt +0 -9
- package/out/note/edit/__next._tree.txt +0 -4
- package/out/note/edit/__next.note.edit.__PAGE__.txt +0 -9
- package/out/note/edit/__next.note.edit.txt +0 -5
- package/out/note/edit/__next.note.txt +0 -5
- package/out/note/edit/index.html +0 -15
- package/out/note/edit/index.txt +0 -24
- /package/out/_next/static/{sIuUKxnnGU7K9Tu9UDKE8 → t7ZIeQpVvjz4a7-5Tt-VK}/_buildManifest.js +0 -0
- /package/out/_next/static/{sIuUKxnnGU7K9Tu9UDKE8 → t7ZIeQpVvjz4a7-5Tt-VK}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{sIuUKxnnGU7K9Tu9UDKE8 → t7ZIeQpVvjz4a7-5Tt-VK}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function getApiErrorStatus(err) {
|
|
2
|
+
switch (err.code) {
|
|
3
|
+
case 'VALIDATION_ERROR':
|
|
4
|
+
case 'PATH_SECURITY_ERROR':
|
|
5
|
+
case 'FILE_SIZE_ERROR':
|
|
6
|
+
return 400
|
|
7
|
+
case 'PEER_NOT_FOUND':
|
|
8
|
+
return 503
|
|
9
|
+
case 'INTEGRITY_ERROR':
|
|
10
|
+
return 422
|
|
11
|
+
case 'CONFLICT':
|
|
12
|
+
return 409
|
|
13
|
+
case 'PERMISSION_ERROR':
|
|
14
|
+
return 403
|
|
15
|
+
case 'ENGINE_NOT_INITIALIZED':
|
|
16
|
+
return 503
|
|
17
|
+
default:
|
|
18
|
+
return 500
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function errorJson(c, err) {
|
|
23
|
+
return c.json(
|
|
24
|
+
{
|
|
25
|
+
error: err.message,
|
|
26
|
+
code: err.code || 'UNKNOWN',
|
|
27
|
+
},
|
|
28
|
+
getApiErrorStatus(err)
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function badRequestOrAppError(c, err) {
|
|
33
|
+
if (err.code) return errorJson(c, err)
|
|
34
|
+
return c.json({ error: err.message }, 400)
|
|
35
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const NODE_LOG_FILTERS = {
|
|
2
|
+
join: ['join', 'joined', 'topic'],
|
|
3
|
+
pull: ['pull', 'p2p'],
|
|
4
|
+
verify: ['verify', 'verified', 'integrity', 'download:success'],
|
|
5
|
+
serve: ['seed', 'seeding', 'holding', 'publish:success', 'topic:joined'],
|
|
6
|
+
error: ['error', 'failed', 'fail'],
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getNodeLogSearchText(log = {}) {
|
|
10
|
+
let dataText = ''
|
|
11
|
+
try {
|
|
12
|
+
dataText = JSON.stringify(log.data || {})
|
|
13
|
+
} catch {}
|
|
14
|
+
|
|
15
|
+
return [log.level, log.event, log.message, dataText]
|
|
16
|
+
.map(value => String(value || '').toLowerCase())
|
|
17
|
+
.join(' ')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function matchesNodeLogFilter(log, filter) {
|
|
21
|
+
const normalized = String(filter || 'all')
|
|
22
|
+
.trim()
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
if (!normalized || normalized === 'all') return true
|
|
25
|
+
|
|
26
|
+
const text = getNodeLogSearchText(log)
|
|
27
|
+
if (normalized === 'error') {
|
|
28
|
+
return (
|
|
29
|
+
log.level === 'error' ||
|
|
30
|
+
NODE_LOG_FILTERS.error.some(term => text.includes(term))
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const terms = NODE_LOG_FILTERS[normalized] || [normalized]
|
|
35
|
+
return terms.some(term => text.includes(term))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function listFilteredNodeLogs(nodeLogger, options = {}) {
|
|
39
|
+
const limit = Math.max(1, Math.min(Number(options.limit) || 100, 1000))
|
|
40
|
+
const filter = String(options.filter || 'all')
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
const query = String(options.query || '')
|
|
44
|
+
.trim()
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
const logs = nodeLogger
|
|
47
|
+
.list(1000)
|
|
48
|
+
.filter(log => matchesNodeLogFilter(log, filter))
|
|
49
|
+
.filter(log => !query || getNodeLogSearchText(log).includes(query))
|
|
50
|
+
.slice(0, limit)
|
|
51
|
+
|
|
52
|
+
return { filter, query, logs }
|
|
53
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { DEFAULT_NODE_HOST } from '../node/config.js'
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const PACKAGE_JSON = readPackageJson()
|
|
8
|
+
|
|
9
|
+
function readPackageJson() {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(
|
|
12
|
+
fs.readFileSync(
|
|
13
|
+
path.join(__dirname, '..', '..', '..', 'package.json'),
|
|
14
|
+
'utf-8'
|
|
15
|
+
)
|
|
16
|
+
)
|
|
17
|
+
} catch {
|
|
18
|
+
return { version: '0.0.0' }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getNetworkAddresses(appPort) {
|
|
23
|
+
return {
|
|
24
|
+
port: appPort,
|
|
25
|
+
addresses: [
|
|
26
|
+
{ type: 'local', ip: 'localhost', label: '本机', iface: 'loopback' },
|
|
27
|
+
],
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function buildNodeStatus(engine, configStore, appPort) {
|
|
32
|
+
const config = configStore.getNodeConfig()
|
|
33
|
+
const { remoteInvites, ...publicConfig } = config
|
|
34
|
+
const remoteInviteCount = remoteInvites.length
|
|
35
|
+
const storage = await engine.getStorageStats()
|
|
36
|
+
const network = engine.getNetworkStatus()
|
|
37
|
+
const holdings = engine.listHoldings()
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
status: 'online',
|
|
41
|
+
version: PACKAGE_JSON.version,
|
|
42
|
+
uptimeSeconds: Math.floor(process.uptime()),
|
|
43
|
+
nodeId: engine.getNodeId(),
|
|
44
|
+
host: DEFAULT_NODE_HOST,
|
|
45
|
+
port: appPort,
|
|
46
|
+
listen: getNetworkAddresses(appPort),
|
|
47
|
+
dataPath: configStore.getDataPath(),
|
|
48
|
+
config: {
|
|
49
|
+
...publicConfig,
|
|
50
|
+
remoteInviteCount,
|
|
51
|
+
remoteInviteConfigured: remoteInviteCount > 0,
|
|
52
|
+
},
|
|
53
|
+
policy: {
|
|
54
|
+
maxFileSizeBytes: config.maxFileSizeBytes,
|
|
55
|
+
},
|
|
56
|
+
capacity: {
|
|
57
|
+
configuredBytes: config.capacityBytes,
|
|
58
|
+
usedBytes: storage.used,
|
|
59
|
+
freeBytes: Math.max(0, config.capacityBytes - storage.used),
|
|
60
|
+
},
|
|
61
|
+
storage,
|
|
62
|
+
network,
|
|
63
|
+
holdings,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function buildOpenApiSpec(appPort) {
|
|
68
|
+
return {
|
|
69
|
+
openapi: '3.1.0',
|
|
70
|
+
info: {
|
|
71
|
+
title: 'MostBox Node Daemon API',
|
|
72
|
+
version: PACKAGE_JSON.version,
|
|
73
|
+
},
|
|
74
|
+
servers: [{ url: `http://localhost:${appPort}` }],
|
|
75
|
+
paths: {
|
|
76
|
+
'/api/node/status': {
|
|
77
|
+
get: {
|
|
78
|
+
summary: 'Get node daemon status',
|
|
79
|
+
responses: { 200: { description: 'Node status' } },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
'/api/node/config': {
|
|
83
|
+
get: {
|
|
84
|
+
summary: 'Get node daemon config',
|
|
85
|
+
responses: { 200: { description: 'Node config' } },
|
|
86
|
+
},
|
|
87
|
+
post: {
|
|
88
|
+
summary: 'Update node daemon config',
|
|
89
|
+
responses: { 200: { description: 'Updated config' } },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
'/api/node/policy': {
|
|
93
|
+
get: {
|
|
94
|
+
summary: 'Get local storage limits',
|
|
95
|
+
responses: { 200: { description: 'Storage limits' } },
|
|
96
|
+
},
|
|
97
|
+
post: {
|
|
98
|
+
summary: 'Update local storage limits',
|
|
99
|
+
responses: { 200: { description: 'Updated policy' } },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
'/api/node/policy/evaluate': {
|
|
103
|
+
post: {
|
|
104
|
+
summary: 'Evaluate a local file against storage limits',
|
|
105
|
+
responses: { 200: { description: 'Storage limit decision' } },
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
'/api/node/holdings': {
|
|
109
|
+
get: {
|
|
110
|
+
summary: 'List CID replicas held by this node',
|
|
111
|
+
responses: { 200: { description: 'Node holdings' } },
|
|
112
|
+
},
|
|
113
|
+
post: {
|
|
114
|
+
summary: 'Add a held CID replica record and join its topic',
|
|
115
|
+
responses: { 200: { description: 'Created holding' } },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
'/api/node/logs': {
|
|
119
|
+
get: {
|
|
120
|
+
summary: 'Read recent node daemon logs',
|
|
121
|
+
responses: { 200: { description: 'Node logs' } },
|
|
122
|
+
},
|
|
123
|
+
delete: {
|
|
124
|
+
summary: 'Clear node daemon logs',
|
|
125
|
+
responses: { 200: { description: 'Logs cleared' } },
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
'/api/node/diagnostics': {
|
|
129
|
+
get: {
|
|
130
|
+
summary: 'Export node diagnostics snapshot',
|
|
131
|
+
responses: { 200: { description: 'Diagnostics snapshot' } },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
'/api/p2p/pull': {
|
|
135
|
+
post: {
|
|
136
|
+
summary: 'Pull a full file replica by CID',
|
|
137
|
+
responses: { 200: { description: 'Pull task result' } },
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function getPackageVersion() {
|
|
145
|
+
return PACKAGE_JSON.version
|
|
146
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { serveStatic } from '@hono/node-server/serve-static'
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
|
|
8
|
+
const MIME_TYPES = {
|
|
9
|
+
'.html': 'text/html; charset=utf-8',
|
|
10
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
11
|
+
'.css': 'text/css; charset=utf-8',
|
|
12
|
+
'.json': 'application/json',
|
|
13
|
+
'.png': 'image/png',
|
|
14
|
+
'.jpg': 'image/jpeg',
|
|
15
|
+
'.jpeg': 'image/jpeg',
|
|
16
|
+
'.gif': 'image/gif',
|
|
17
|
+
'.webp': 'image/webp',
|
|
18
|
+
'.svg': 'image/svg+xml',
|
|
19
|
+
'.ico': 'image/x-icon',
|
|
20
|
+
'.mp4': 'video/mp4',
|
|
21
|
+
'.webm': 'video/webm',
|
|
22
|
+
'.ogg': 'video/ogg',
|
|
23
|
+
'.mp3': 'audio/mpeg',
|
|
24
|
+
'.wav': 'audio/wav',
|
|
25
|
+
'.flac': 'audio/flac',
|
|
26
|
+
'.aac': 'audio/aac',
|
|
27
|
+
'.m4a': 'audio/mp4',
|
|
28
|
+
'.opus': 'audio/opus',
|
|
29
|
+
'.woff2': 'font/woff2',
|
|
30
|
+
'.woff': 'font/woff',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getMimeType(fileName) {
|
|
34
|
+
const ext = path.extname(fileName).toLowerCase()
|
|
35
|
+
return MIME_TYPES[ext] || 'application/octet-stream'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function registerStaticRoutes(app) {
|
|
39
|
+
const publicDir = path.join(__dirname, '..', '..', '..', 'out')
|
|
40
|
+
|
|
41
|
+
app.get('/static/*', serveStatic({ root: './out' }))
|
|
42
|
+
app.get('/_next/*', serveStatic({ root: './out' }))
|
|
43
|
+
|
|
44
|
+
app.all('/api/*', c => {
|
|
45
|
+
return c.json({ error: 'Not found' }, 404)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
app.get('*', async c => {
|
|
49
|
+
const pathname = c.req.path
|
|
50
|
+
const filePath = path.join(publicDir, pathname)
|
|
51
|
+
const resolved = path.resolve(filePath)
|
|
52
|
+
const resolvedPublic = path.resolve(publicDir)
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
!resolved.startsWith(resolvedPublic + path.sep) &&
|
|
56
|
+
resolved !== resolvedPublic
|
|
57
|
+
) {
|
|
58
|
+
return c.json({ error: 'Not found' }, 404)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (fs.existsSync(filePath)) {
|
|
62
|
+
const stat = fs.statSync(filePath)
|
|
63
|
+
if (stat.isFile()) {
|
|
64
|
+
c.header('Content-Type', getMimeType(filePath))
|
|
65
|
+
return c.body(fs.readFileSync(filePath))
|
|
66
|
+
}
|
|
67
|
+
if (stat.isDirectory()) {
|
|
68
|
+
const dirIndex = path.join(filePath, 'index.html')
|
|
69
|
+
if (fs.existsSync(dirIndex)) {
|
|
70
|
+
c.header('Content-Type', 'text/html; charset=utf-8')
|
|
71
|
+
return c.body(fs.readFileSync(dirIndex, 'utf-8'))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const indexPath = path.join(publicDir, 'index.html')
|
|
77
|
+
if (fs.existsSync(indexPath)) {
|
|
78
|
+
c.header('Content-Type', 'text/html; charset=utf-8')
|
|
79
|
+
return c.body(fs.readFileSync(indexPath, 'utf-8'))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return c.json({ error: 'Not found' }, 404)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import Busboy from 'busboy'
|
|
5
|
+
import { MAX_FILE_SIZE } from '../config.js'
|
|
6
|
+
|
|
7
|
+
export const UPLOAD_TMP_DIR = path.join(os.tmpdir(), 'most-box-uploads')
|
|
8
|
+
|
|
9
|
+
function decodeFilenameFromHeader(headerStr) {
|
|
10
|
+
if (!headerStr) return null
|
|
11
|
+
|
|
12
|
+
const filenameStarMatch = headerStr.match(
|
|
13
|
+
/filename\*=(?:UTF-8''|utf-8'')([^;\r\n]+)/i
|
|
14
|
+
)
|
|
15
|
+
if (filenameStarMatch) {
|
|
16
|
+
return decodeURIComponent(filenameStarMatch[1])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const filenameMatch = headerStr.match(/filename="([^"]+)"/)
|
|
20
|
+
if (filenameMatch) {
|
|
21
|
+
const rawFilename = filenameMatch[1]
|
|
22
|
+
try {
|
|
23
|
+
const buf = Buffer.from(rawFilename, 'latin1')
|
|
24
|
+
const decoded = buf.toString('utf8')
|
|
25
|
+
if (decoded.includes('\ufffd')) {
|
|
26
|
+
return rawFilename
|
|
27
|
+
}
|
|
28
|
+
return decoded
|
|
29
|
+
} catch {
|
|
30
|
+
return rawFilename
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const filenamePlainMatch = headerStr.match(/filename=([^;\r\n]+)/)
|
|
35
|
+
if (filenamePlainMatch) {
|
|
36
|
+
return filenamePlainMatch[1].trim()
|
|
37
|
+
}
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function parseMultipartBusboy(req, maxUploadSize = MAX_FILE_SIZE) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
if (!fs.existsSync(UPLOAD_TMP_DIR)) {
|
|
44
|
+
fs.mkdirSync(UPLOAD_TMP_DIR, { recursive: true })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const busboy = Busboy({
|
|
48
|
+
headers: req.headers,
|
|
49
|
+
limits: {
|
|
50
|
+
fileSize: maxUploadSize,
|
|
51
|
+
files: 1,
|
|
52
|
+
fields: 0,
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const result = { filePath: null, filename: null }
|
|
57
|
+
let fileSize = 0
|
|
58
|
+
let writeStream = null
|
|
59
|
+
let tempPath = null
|
|
60
|
+
|
|
61
|
+
busboy.on('file', (name, stream, info) => {
|
|
62
|
+
result.filename = decodeFilenameFromHeader(`filename="${info.filename}"`)
|
|
63
|
+
tempPath = path.join(
|
|
64
|
+
UPLOAD_TMP_DIR,
|
|
65
|
+
`upload_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
|
66
|
+
)
|
|
67
|
+
writeStream = fs.createWriteStream(tempPath)
|
|
68
|
+
|
|
69
|
+
stream.on('data', chunk => {
|
|
70
|
+
fileSize += chunk.length
|
|
71
|
+
if (fileSize > maxUploadSize) {
|
|
72
|
+
stream.destroy()
|
|
73
|
+
writeStream.destroy()
|
|
74
|
+
fs.unlink(tempPath, () => {})
|
|
75
|
+
reject(new Error('File too large'))
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
stream.on('error', () => {
|
|
81
|
+
if (tempPath) fs.unlink(tempPath, () => {})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
stream.pipe(writeStream)
|
|
85
|
+
|
|
86
|
+
writeStream.on('finish', () => {
|
|
87
|
+
result.filePath = tempPath
|
|
88
|
+
resolve(result)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
writeStream.on('error', err => {
|
|
92
|
+
if (tempPath) fs.unlink(tempPath, () => {})
|
|
93
|
+
reject(err)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
busboy.on('error', err => {
|
|
98
|
+
if (tempPath) fs.unlink(tempPath, () => {})
|
|
99
|
+
reject(err)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
busboy.on('close', () => {
|
|
103
|
+
if (!result.filename) {
|
|
104
|
+
resolve(null)
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
req.on('error', err => {
|
|
109
|
+
if (tempPath) fs.unlink(tempPath, () => {})
|
|
110
|
+
reject(err)
|
|
111
|
+
})
|
|
112
|
+
req.pipe(busboy)
|
|
113
|
+
})
|
|
114
|
+
}
|