@dotrino/content 0.3.13 → 0.4.1
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 +0 -0
- package/bin/cli.js +29 -3
- package/lib/public.js +0 -0
- package/lib/ref.js +0 -0
- package/package.json +6 -4
- package/src/agent.js +0 -0
- package/src/announce.js +34 -3
- package/src/blobstore-s3.js +0 -0
- package/src/blobstore.js +0 -0
- package/src/db.js +0 -0
- package/src/fetch.js +0 -0
- package/src/index.js +0 -0
- package/src/node.js +0 -0
- package/src/ops.js +0 -0
- package/src/public.js +0 -0
- package/src/s3.js +0 -0
- package/src/server.js +0 -0
- package/src/storage.js +0 -0
- package/src/vaultEnv.js +0 -0
package/README.md
CHANGED
|
File without changes
|
package/bin/cli.js
CHANGED
|
@@ -26,9 +26,14 @@ import { ContentNode } from '../src/node.js'
|
|
|
26
26
|
import { createServer } from '../src/server.js'
|
|
27
27
|
import { createPublicServer, DEFAULT_PUBLIC_PORT, DEFAULT_MAX_BYTES, DEFAULT_RATE_PER_MIN } from '../src/public.js'
|
|
28
28
|
import { isLinked, linkDir, startContentAgent } from '../src/agent.js'
|
|
29
|
+
import { createRequire } from 'node:module'
|
|
30
|
+
|
|
31
|
+
const { version: VERSION } = createRequire(import.meta.url)('../package.json')
|
|
29
32
|
|
|
30
33
|
const USAGE = `uso:
|
|
31
34
|
dotrino-content enroll <código>
|
|
35
|
+
dotrino-content info [--json] qué aparato es este node: su ID (el de «dotrino-vault members»),
|
|
36
|
+
su bóveda y sus permisos. Sin red.
|
|
32
37
|
dotrino-content start [--port 3777] [--dir <ruta>] [--max-gb <n>] [--max-blob-mb <n>] [--gc-min <min>] [--no-agent]
|
|
33
38
|
[--public] [--public-port 3778] [--public-host 0.0.0.0] [--public-max-kb 512]
|
|
34
39
|
[--public-rate 60] [--public-egress-gb <n>] [--public-url https://…] [--app-url https://…]
|
|
@@ -53,6 +58,7 @@ const { values, positionals } = parseArgs({
|
|
|
53
58
|
'public-url': { type: 'string' },
|
|
54
59
|
'app-url': { type: 'string' },
|
|
55
60
|
'public-index': { type: 'boolean' },
|
|
61
|
+
json: { type: 'boolean' },
|
|
56
62
|
help: { type: 'boolean', short: 'h' }
|
|
57
63
|
}
|
|
58
64
|
})
|
|
@@ -63,11 +69,24 @@ if (values.help) {
|
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
const cmd = positionals[0] || 'start'
|
|
66
|
-
if (cmd !== 'start' && cmd !== 'enroll') {
|
|
72
|
+
if (cmd !== 'start' && cmd !== 'enroll' && cmd !== 'info') {
|
|
67
73
|
console.error(`comando desconocido: ${cmd}\n${USAGE}`)
|
|
68
74
|
process.exit(1)
|
|
69
75
|
}
|
|
70
76
|
|
|
77
|
+
// `info`: la pieza común de todos los comandos (CONVENCIONES §15.1). Lo que se viene a
|
|
78
|
+
// mirar es el ID, para buscarlo en el acta.
|
|
79
|
+
if (cmd === 'info') {
|
|
80
|
+
const { loadLink } = await import('@dotrino/remote-agent/link')
|
|
81
|
+
const { deviceInfo, formatDeviceInfo } = await import('@dotrino/vault/device-info')
|
|
82
|
+
const dir = linkDir()
|
|
83
|
+
const link = loadLink(dir)
|
|
84
|
+
if (!link) { console.error(`este node no está enlazado (${dir}): dotrino-content enroll <código>`); process.exit(1) }
|
|
85
|
+
const info = await deviceInfo(link, { kind: 'content', ns: 'content', version: VERSION, dir })
|
|
86
|
+
console.log(values.json ? JSON.stringify(info, null, 2) : formatDeviceInfo(info))
|
|
87
|
+
process.exit(0)
|
|
88
|
+
}
|
|
89
|
+
|
|
71
90
|
/**
|
|
72
91
|
* Enrolamiento: el flujo endurecido del ecosistema, tal cual lo hace la terminal.
|
|
73
92
|
* Este node genera su llave, MUESTRA un código y espera a que lo apruebes tipeando
|
|
@@ -149,10 +168,17 @@ node.gc()
|
|
|
149
168
|
// propia ACL, su límite por IP y su techo de salida: no se activa por descuido.
|
|
150
169
|
server.listen(port, '127.0.0.1', () => {
|
|
151
170
|
const s = node.stats()
|
|
152
|
-
console.log(`dotrino-content en http://127.0.0.1:${port} · datos: ${dir}`)
|
|
171
|
+
console.log(`dotrino-content ${VERSION} en http://127.0.0.1:${port} · datos: ${dir}`)
|
|
153
172
|
console.log(`blobs: ${s.blobs} · bytes: ${s.bytes}${maxBytes ? ` / ${maxBytes}` : ''}`)
|
|
154
173
|
})
|
|
155
174
|
|
|
175
|
+
// §15: una vez al día mira si hay versión nueva y lo dice en el log. Solo avisa.
|
|
176
|
+
const { watchForUpdate } = await import('@dotrino/update')
|
|
177
|
+
watchForUpdate({
|
|
178
|
+
current: VERSION, source: 'npm', pkg: '@dotrino/content',
|
|
179
|
+
onNewer: (r) => console.log(`[content] version ${r.version} is available (running ${r.current}): npm i -g @dotrino/content@${r.version}`)
|
|
180
|
+
})
|
|
181
|
+
|
|
156
182
|
// Plano de control: solo si este node ya está enlazado a un vault. Sin enlace sigue
|
|
157
183
|
// siendo lo de antes (un node local), y se dice en voz alta para que nadie crea que
|
|
158
184
|
// tiene administración remota cuando no la tiene.
|
|
@@ -161,7 +187,7 @@ if (values['no-agent']) {
|
|
|
161
187
|
console.log('plano de control: apagado (--no-agent)')
|
|
162
188
|
} else if (isLinked()) {
|
|
163
189
|
try {
|
|
164
|
-
agent = await startContentAgent({ node })
|
|
190
|
+
agent = await startContentAgent({ node, version: VERSION })
|
|
165
191
|
} catch (e) {
|
|
166
192
|
console.error(`plano de control: no arrancó (${e.message})`)
|
|
167
193
|
}
|
package/lib/public.js
CHANGED
|
File without changes
|
package/lib/ref.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/content",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Nodo de contenido del ecosistema Dotrino: guarda los bytes del usuario direccionados por su hash (cid), en su propia maquina y, si el dueno quiere, respaldados en su propio bucket (R2, Backblaze, Hetzner, Storj o S3). Lo publico sale por URL directa; lo privado, cifrado y solo por la red Dotrino.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,9 +23,11 @@
|
|
|
23
23
|
"typescript": "5.9.3"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@dotrino/identity": "0.
|
|
27
|
-
"@dotrino/
|
|
28
|
-
"@dotrino/
|
|
26
|
+
"@dotrino/identity": "0.105.0",
|
|
27
|
+
"@dotrino/proxy-client": "0.25.0",
|
|
28
|
+
"@dotrino/remote-agent": "0.12.0",
|
|
29
|
+
"@dotrino/update": "0.2.2",
|
|
30
|
+
"@dotrino/vault": "0.75.0"
|
|
29
31
|
},
|
|
30
32
|
"keywords": [
|
|
31
33
|
"dotrino",
|
package/src/agent.js
CHANGED
|
File without changes
|
package/src/announce.js
CHANGED
|
@@ -38,6 +38,21 @@ export { channelFor }
|
|
|
38
38
|
/** Cada cuánto se re-publica (el proxio caduca las entradas de canal). */
|
|
39
39
|
export const REPUBLISH_MS = 4 * 60 * 1000
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Cada cuánto se reintenta un anuncio que falló. No se espera al tic de 4 min: el fallo
|
|
43
|
+
* típico es un proxio recién reiniciado (cada despliegue lo hace) cuya malla con el otro
|
|
44
|
+
* aún no está lista, y eso se arregla en segundos. Mientras tanto quien esté conectado a
|
|
45
|
+
* ese proxio no encuentra el node.
|
|
46
|
+
*/
|
|
47
|
+
export const RETRY_MS = 30 * 1000
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Cuántos reintentos seguidos se aguantan en silencio antes de llamarlo error (~2 min).
|
|
51
|
+
* Un proxio que se está reiniciando no es un fallo y no debe salir en el log de errores;
|
|
52
|
+
* uno que lleva dos minutos sin dejar anunciarse, sí.
|
|
53
|
+
*/
|
|
54
|
+
export const FAILURES_BEFORE_ERROR = 4
|
|
55
|
+
|
|
41
56
|
/**
|
|
42
57
|
* Publica este node en el canal de su dueño, en todos los proxios conocidos, y lo
|
|
43
58
|
* mantiene publicado. Best-effort a propósito: si el proxy está caído, el node
|
|
@@ -45,17 +60,19 @@ export const REPUBLISH_MS = 4 * 60 * 1000
|
|
|
45
60
|
* siguiente tic lo vuelve a intentar. Un anuncio perdido no rompe nada; lo único
|
|
46
61
|
* que pasa es que un tercero no lo encuentra hasta que vuelva.
|
|
47
62
|
*
|
|
48
|
-
* @param {{ client: any, owner: string, quiet?: boolean, intervalMs?: number }} opts
|
|
63
|
+
* @param {{ client: any, owner: string, quiet?: boolean, intervalMs?: number, retryMs?: number }} opts
|
|
49
64
|
* client: el `WebSocketProxyClient` YA conectado del agente (`ra.client`) — no se
|
|
50
65
|
* abre otro: dos conexiones del mismo aparato son dos identidades de transporte.
|
|
51
66
|
* @returns {{ channels: () => string[], close: () => void }}
|
|
52
67
|
*/
|
|
53
|
-
export function startAnnounce ({ client, owner, quiet = false, intervalMs = REPUBLISH_MS }) {
|
|
68
|
+
export function startAnnounce ({ client, owner, quiet = false, intervalMs = REPUBLISH_MS, retryMs = RETRY_MS }) {
|
|
54
69
|
if (!client) throw new Error('startAnnounce: client is required')
|
|
55
70
|
if (!owner) throw new Error('startAnnounce: owner is required')
|
|
56
71
|
|
|
57
72
|
let stopped = false
|
|
58
73
|
let current = []
|
|
74
|
+
let retry = null
|
|
75
|
+
const failures = new Map() // canal → fallos seguidos
|
|
59
76
|
|
|
60
77
|
/** Los proxios donde hay que anunciarse: el que nos atiende y los que conoce. */
|
|
61
78
|
const targets = () => {
|
|
@@ -72,10 +89,23 @@ export function startAnnounce ({ client, owner, quiet = false, intervalMs = REPU
|
|
|
72
89
|
try {
|
|
73
90
|
await client.publish(name, { app: 'content', owner })
|
|
74
91
|
done.push(name)
|
|
92
|
+
const antes = failures.get(name) || 0
|
|
93
|
+
failures.delete(name)
|
|
94
|
+
if (antes >= FAILURES_BEFORE_ERROR && !quiet) console.log(`[content] announce on ${name} works again`)
|
|
75
95
|
} catch (e) {
|
|
76
|
-
|
|
96
|
+
const n = (failures.get(name) || 0) + 1
|
|
97
|
+
failures.set(name, n)
|
|
98
|
+
if (quiet) continue
|
|
99
|
+
// Primer fallo: casi siempre un proxio que se acaba de reiniciar. Se dice sin alarma.
|
|
100
|
+
if (n === 1) console.log(`[content] announce on ${name} failed (${e.message}); retrying every ${retryMs / 1000} s`)
|
|
101
|
+
// Si sigue, ya no es un reinicio: error, una sola vez hasta que se recupere.
|
|
102
|
+
else if (n === FAILURES_BEFORE_ERROR) console.error(`[content] still cannot announce on ${name} after ${Math.round(n * retryMs / 60000)} min: ${e.message}`)
|
|
77
103
|
}
|
|
78
104
|
}
|
|
105
|
+
if (failures.size && !retry && !stopped) {
|
|
106
|
+
retry = setTimeout(() => { retry = null; publishAll() }, retryMs)
|
|
107
|
+
retry.unref?.()
|
|
108
|
+
}
|
|
79
109
|
// Se avisa cuando CAMBIA en cuántos proxios está anunciado, no solo la primera vez.
|
|
80
110
|
// Antes solo hablaba al pasar de 0 a algo, así que un arranque que solo alcanzó uno de
|
|
81
111
|
// los dos decía «en 1 proxio(s)» y se callaba para siempre — aunque el tic siguiente ya
|
|
@@ -98,6 +128,7 @@ export function startAnnounce ({ client, owner, quiet = false, intervalMs = REPU
|
|
|
98
128
|
close () {
|
|
99
129
|
stopped = true
|
|
100
130
|
clearInterval(timer)
|
|
131
|
+
if (retry) { clearTimeout(retry); retry = null }
|
|
101
132
|
try { offToken?.() } catch (_) {}
|
|
102
133
|
for (const name of current) { try { client.unpublish(name) } catch (_) {} }
|
|
103
134
|
current = []
|
package/src/blobstore-s3.js
CHANGED
|
File without changes
|
package/src/blobstore.js
CHANGED
|
File without changes
|
package/src/db.js
CHANGED
|
File without changes
|
package/src/fetch.js
CHANGED
|
File without changes
|
package/src/index.js
CHANGED
|
File without changes
|
package/src/node.js
CHANGED
|
File without changes
|
package/src/ops.js
CHANGED
|
File without changes
|
package/src/public.js
CHANGED
|
File without changes
|
package/src/s3.js
CHANGED
|
File without changes
|
package/src/server.js
CHANGED
|
File without changes
|
package/src/storage.js
CHANGED
|
File without changes
|
package/src/vaultEnv.js
CHANGED
|
File without changes
|