most-box 0.1.1 → 0.1.3
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 +39 -19
- package/electron/afterPack.cjs +87 -0
- package/electron/main.js +156 -8
- package/electron/preload.js +6 -0
- package/electron/updateChecker.js +97 -0
- package/electron/updateChecker.test.js +147 -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/04mo7rr..0_1q.js +1 -0
- package/out/_next/static/chunks/06rf3qq5ggs6v.js +1 -0
- package/out/_next/static/chunks/07td.jq7xff84.css +1 -0
- package/out/_next/static/chunks/0_0oph_z1az14.js +1 -0
- package/out/_next/static/chunks/{0qou.u2e2dy48.css → 0adx~d-j05c9d.css} +2 -2
- package/out/_next/static/chunks/0cl7d~7abnk_p.css +1 -0
- package/out/_next/static/chunks/0d306t1wvjpdx.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/0hpev4am9jpmu.css +1 -0
- package/out/_next/static/chunks/0m_5nb6x8qy._.js +1 -0
- package/out/_next/static/chunks/0n.ayxmsar6e5.js +1 -0
- package/out/_next/static/chunks/{0usvo~vu7r8np.js → 0o9ce4cyf76by.js} +1 -1
- package/out/_next/static/chunks/0olqjomda37-e.js +1 -0
- package/out/_next/static/chunks/{0o98f1yq..o.8.js → 0pt.5cg1t09qs.js} +1 -1
- package/out/_next/static/chunks/0qgx9t4jx16ua.css +1 -0
- package/out/_next/static/chunks/0s~g.l~x049o2.js +1 -0
- package/out/_next/static/chunks/0ukyg~tkm~h2m.css +1 -0
- package/out/_next/static/chunks/0voe1.ttrh84k.css +1 -0
- package/out/_next/static/chunks/0wtf0xsiicxx6.js +1 -0
- package/out/_next/static/chunks/0x.ky97owcxxs.js +1 -0
- package/out/_next/static/chunks/0xdwau5k2augv.css +4 -0
- package/out/_next/static/chunks/0ysj5b94vu4ri.js +1 -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/17cwkb2yn_akx.js +1 -0
- package/out/_next/static/chunks/184hxsuf-5c84.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 +37 -33
- 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 -14
- package/out/download/__next.download.txt +3 -3
- package/out/download/index.html +2 -2
- package/out/download/index.txt +37 -33
- package/out/favicon.ico +0 -0
- package/out/gandengyan/__next._full.txt +25 -0
- package/out/{changelog → gandengyan}/__next._head.txt +3 -3
- package/out/{docs → gandengyan}/__next._index.txt +8 -8
- package/out/gandengyan/__next._tree.txt +5 -0
- package/out/gandengyan/__next.gandengyan.__PAGE__.txt +10 -0
- package/out/gandengyan/__next.gandengyan.txt +5 -0
- package/out/gandengyan/index.html +15 -0
- package/out/gandengyan/index.txt +25 -0
- 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 +44 -35
- package/public/favicon.ico +0 -0
- package/server/index.js +142 -1304
- 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/games/gandengyan.js +675 -0
- package/server/src/http/access.js +127 -0
- package/server/src/http/app.js +1102 -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 +305 -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/0ho~log~~-jwp.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._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._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 → aPEZ4zaaR5W3WpSZ0dFsa}/_buildManifest.js +0 -0
- /package/out/_next/static/{sIuUKxnnGU7K9Tu9UDKE8 → aPEZ4zaaR5W3WpSZ0dFsa}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{sIuUKxnnGU7K9Tu9UDKE8 → aPEZ4zaaR5W3WpSZ0dFsa}/_ssgManifest.js +0 -0
package/server/src/config.js
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { parseMostLink, validateCidString } from './cid.js'
|
|
2
|
+
import { sanitizeFilename } from '../utils/security.js'
|
|
3
|
+
import { ValidationError } from '../utils/errors.js'
|
|
4
|
+
|
|
5
|
+
const CHANNEL_ATTACHMENT_KINDS = new Set([
|
|
6
|
+
'image',
|
|
7
|
+
'video',
|
|
8
|
+
'audio',
|
|
9
|
+
'text',
|
|
10
|
+
'file',
|
|
11
|
+
])
|
|
12
|
+
|
|
13
|
+
export function normalizeChannelAttachment(input) {
|
|
14
|
+
if (input === undefined || input === null) return null
|
|
15
|
+
if (typeof input !== 'object' || Array.isArray(input)) {
|
|
16
|
+
throw new ValidationError('attachment must be an object')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const kind = String(input.kind || '').trim()
|
|
20
|
+
if (!CHANNEL_ATTACHMENT_KINDS.has(kind)) {
|
|
21
|
+
throw new ValidationError('Invalid attachment kind')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const cid = String(input.cid || '').trim()
|
|
25
|
+
const cidValidation = validateCidString(cid)
|
|
26
|
+
if (!cidValidation.valid) {
|
|
27
|
+
throw new ValidationError(cidValidation.error)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const fileName = sanitizeFilename(String(input.fileName || ''))
|
|
31
|
+
if (!fileName || fileName === 'unnamed_file') {
|
|
32
|
+
throw new ValidationError('attachment fileName is required')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const parsed = parseMostLink(String(input.link || '').trim())
|
|
36
|
+
if (parsed.error) {
|
|
37
|
+
throw new ValidationError(parsed.error)
|
|
38
|
+
}
|
|
39
|
+
if (parsed.cid !== cid) {
|
|
40
|
+
throw new ValidationError('attachment link CID mismatch')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const linkFileName = sanitizeFilename(parsed.fileName)
|
|
44
|
+
if (linkFileName !== fileName) {
|
|
45
|
+
throw new ValidationError('attachment link filename mismatch')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const attachment = {
|
|
49
|
+
kind,
|
|
50
|
+
cid,
|
|
51
|
+
fileName,
|
|
52
|
+
link: `most://${cid}?filename=${encodeURIComponent(fileName)}`,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof input.mimeType === 'string' && input.mimeType.length <= 100) {
|
|
56
|
+
attachment.mimeType = input.mimeType
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (input.size !== undefined && input.size !== null) {
|
|
60
|
+
const size = Number(input.size)
|
|
61
|
+
if (!Number.isFinite(size) || size < 0) {
|
|
62
|
+
throw new ValidationError('attachment size must be a non-negative number')
|
|
63
|
+
}
|
|
64
|
+
attachment.size = Math.floor(size)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return attachment
|
|
68
|
+
}
|
package/server/src/core/cid.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import fs from 'node:fs'
|
|
7
7
|
import { Readable } from 'node:stream'
|
|
8
8
|
import { importer } from 'ipfs-unixfs-importer'
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
export { validateCidString, parseMostLink } from './mostLink.js'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 用于 CID 计算的虚拟 Blockstore
|
|
@@ -89,90 +90,3 @@ export async function calculateCid(content) {
|
|
|
89
90
|
size: totalSize,
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* 验证 CID 字符串
|
|
95
|
-
* @param {string} cidString - 要验证的 CID 字符串
|
|
96
|
-
* @returns {{ valid: boolean, error?: string }}
|
|
97
|
-
*/
|
|
98
|
-
export function validateCidString(cidString) {
|
|
99
|
-
if (!cidString || typeof cidString !== 'string') {
|
|
100
|
-
return { valid: false, error: 'CID must be a non-empty string' }
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let parsed
|
|
104
|
-
try {
|
|
105
|
-
parsed = CID.parse(cidString)
|
|
106
|
-
} catch {
|
|
107
|
-
return {
|
|
108
|
-
valid: false,
|
|
109
|
-
error: 'Invalid CID format',
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (parsed.version !== 1) {
|
|
114
|
-
return {
|
|
115
|
-
valid: false,
|
|
116
|
-
error: 'Invalid CID format: CID v1 required',
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (parsed.multihash.digest.length !== 32) {
|
|
121
|
-
return {
|
|
122
|
-
valid: false,
|
|
123
|
-
error: 'CID digest must be 32 bytes',
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return { valid: true }
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 解析 most:// 链接并提取 CID 与用户可见文件名
|
|
132
|
-
* @param {string} link - most://<cid>?filename=... 格式的链接
|
|
133
|
-
* @returns {{ cid: string, fileName?: string, error?: string }}
|
|
134
|
-
*/
|
|
135
|
-
export function parseMostLink(link) {
|
|
136
|
-
if (!link || typeof link !== 'string') {
|
|
137
|
-
return { cid: '', error: 'Link must be a non-empty string' }
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
let url
|
|
141
|
-
try {
|
|
142
|
-
url = new URL(link)
|
|
143
|
-
} catch {
|
|
144
|
-
return { cid: '', error: 'Link must be a valid most:// URL' }
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (url.protocol !== 'most:') {
|
|
148
|
-
return { cid: '', error: 'Link must use most:// protocol' }
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (url.pathname && url.pathname !== '/') {
|
|
152
|
-
return { cid: '', error: 'Link path is not supported' }
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const cidString = url.hostname
|
|
156
|
-
const fileName = url.searchParams.get('filename')
|
|
157
|
-
const unsupportedParam = [...url.searchParams.keys()].find(
|
|
158
|
-
key => key !== 'filename'
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
const validation = validateCidString(cidString)
|
|
162
|
-
if (!validation.valid) {
|
|
163
|
-
return { cid: '', error: validation.error }
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (!fileName) {
|
|
167
|
-
return { cid: '', error: 'filename is required' }
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (unsupportedParam) {
|
|
171
|
-
return {
|
|
172
|
-
cid: '',
|
|
173
|
-
error: `Unsupported query parameter: ${unsupportedParam}`,
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return { cid: cidString, fileName }
|
|
178
|
-
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import b4a from 'b4a'
|
|
2
|
+
import { CID } from 'multiformats/cid'
|
|
3
|
+
import { validateCidString } from './cid.js'
|
|
4
|
+
import { ValidationError } from '../utils/errors.js'
|
|
5
|
+
|
|
6
|
+
export function getCidInfo(cid) {
|
|
7
|
+
try {
|
|
8
|
+
const validation = validateCidString(cid)
|
|
9
|
+
if (!validation.valid) {
|
|
10
|
+
throw new ValidationError(validation.error)
|
|
11
|
+
}
|
|
12
|
+
const parsedCid = CID.parse(cid)
|
|
13
|
+
const topic = b4a.from(parsedCid.multihash.digest)
|
|
14
|
+
if (topic.length !== 32) {
|
|
15
|
+
throw new ValidationError('CID digest must be 32 bytes')
|
|
16
|
+
}
|
|
17
|
+
const topicHex = b4a.toString(topic, 'hex')
|
|
18
|
+
return {
|
|
19
|
+
topic,
|
|
20
|
+
topicHex,
|
|
21
|
+
driveName: `drive-${topicHex}`,
|
|
22
|
+
}
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (err instanceof ValidationError) {
|
|
25
|
+
throw err
|
|
26
|
+
}
|
|
27
|
+
throw new ValidationError('Invalid CID format')
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 验证 CID 字符串
|
|
5
|
+
* @param {string} cidString - 要验证的 CID 字符串
|
|
6
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
7
|
+
*/
|
|
8
|
+
export function validateCidString(cidString) {
|
|
9
|
+
if (!cidString || typeof cidString !== 'string') {
|
|
10
|
+
return { valid: false, error: 'CID must be a non-empty string' }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let parsed
|
|
14
|
+
try {
|
|
15
|
+
parsed = CID.parse(cidString)
|
|
16
|
+
} catch {
|
|
17
|
+
return {
|
|
18
|
+
valid: false,
|
|
19
|
+
error: 'Invalid CID format',
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (parsed.version !== 1) {
|
|
24
|
+
return {
|
|
25
|
+
valid: false,
|
|
26
|
+
error: 'Invalid CID format: CID v1 required',
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (parsed.multihash.digest.length !== 32) {
|
|
31
|
+
return {
|
|
32
|
+
valid: false,
|
|
33
|
+
error: 'CID digest must be 32 bytes',
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { valid: true }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 解析 most:// 链接并提取 CID 与用户可见文件名
|
|
42
|
+
* @param {string} link - most://<cid>?filename=... 格式的链接
|
|
43
|
+
* @returns {{ cid: string, fileName?: string, error?: string }}
|
|
44
|
+
*/
|
|
45
|
+
export function parseMostLink(link) {
|
|
46
|
+
if (!link || typeof link !== 'string') {
|
|
47
|
+
return { cid: '', error: 'Link must be a non-empty string' }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let url
|
|
51
|
+
try {
|
|
52
|
+
url = new URL(link)
|
|
53
|
+
} catch {
|
|
54
|
+
return { cid: '', error: 'Link must be a valid most:// URL' }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (url.protocol !== 'most:') {
|
|
58
|
+
return { cid: '', error: 'Link must use most:// protocol' }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (url.pathname && url.pathname !== '/') {
|
|
62
|
+
return { cid: '', error: 'Link path is not supported' }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const cidString = url.hostname
|
|
66
|
+
const fileName = url.searchParams.get('filename')
|
|
67
|
+
const unsupportedParam = [...url.searchParams.keys()].find(
|
|
68
|
+
key => key !== 'filename'
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const validation = validateCidString(cidString)
|
|
72
|
+
if (!validation.valid) {
|
|
73
|
+
return { cid: '', error: validation.error }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!fileName) {
|
|
77
|
+
return { cid: '', error: 'filename is required' }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (unsupportedParam) {
|
|
81
|
+
return {
|
|
82
|
+
cid: '',
|
|
83
|
+
error: `Unsupported query parameter: ${unsupportedParam}`,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { cid: cidString, fileName }
|
|
88
|
+
}
|