Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .beads/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
dolt/
embeddeddolt/
proxieddb/
backup/
bd.sock
bd.sock.startlock
sync-state.json
last-touched
.exclusive-lock
daemon.*
push-state.json
*.lock
.beads-credential-key
.local_version
proxied_server_client_info.json
redirect
.sync.lock
export-state/
export-state.json
last_pull
ephemeral.sqlite3*
dolt-server.*
dolt-pprof/
*.corrupt.backup/
.env
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
db.sqlite
bd.db
1 change: 1 addition & 0 deletions .beads/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sync.remote: "git+ssh://git@github.com/kakulukia/bitfeed.git"
7 changes: 7 additions & 0 deletions .beads/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"database": "dolt",
"backend": "dolt",
"dolt_mode": "embedded",
"dolt_database": "bitfeed",
"project_id": "451af4ca-8ba0-4001-b42c-321c0623977e"
}
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
TIMESTAMP.txt

# Local agent/tool state
.agents/
.claude/
.codex/
.codegraph/
AGENTS.md
CLAUDE.md

# Beads / Dolt files
.dolt/
*.db
.beads-credential-key
.beads/proxieddb/
.beads/README.md
.beads/hooks/
.beads/interactions.jsonl

# bv (beads viewer) local config and caches
.bv/
11 changes: 11 additions & 0 deletions .mise.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[tasks."server:compile"]
description = "Compile the server with the Dockerfile toolchain, Elixir 1.11 / OTP 23"
run = "container build --tag bitfeed-server:local server"

[tasks."server:test"]
description = "Run server tests inside the Elixir 1.11 / OTP 23 container toolchain"
run = "server/scripts/test-elixir11-container.sh"

[tasks."server:test-mempool"]
description = "Run the mempool regression test inside the Elixir 1.11 / OTP 23 container toolchain"
run = "server/scripts/test-elixir11-container.sh test/mempool_test.exs"
69 changes: 47 additions & 22 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public --single"
"start": "sirv public --single",
"start:proxy": "node scripts/umbrel-dev-server.mjs"
},
"dependencies": {
"@babel/core": "^7.16.5",
Expand Down
94 changes: 94 additions & 0 deletions client/scripts/umbrel-dev-server.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import fs from 'node:fs'
import http from 'node:http'
import https from 'node:https'
import net from 'node:net'
import path from 'node:path'
import tls from 'node:tls'
import { fileURLToPath } from 'node:url'

const port = Number(process.env.PORT || 5001)
const host = process.env.HOST || '127.0.0.1'
const targetUrl = (value, fallbackProtocol) =>
new URL(value.includes('://') ? value : `${fallbackProtocol}://${value}`)
const wsTarget = targetUrl(process.env.BITFEED_WS_TARGET || 'wss://bits.monospace.live', 'ws')
const apiTarget = targetUrl(process.env.BITFEED_API_TARGET || 'https://bits.monospace.live', 'https')
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../public/build')

const types = {
'.css': 'text/css',
'.html': 'text/html',
'.ico': 'image/x-icon',
'.js': 'text/javascript',
'.json': 'application/json',
'.map': 'application/json',
'.png': 'image/png',
'.svg': 'image/svg+xml',
'.webmanifest': 'application/manifest+json',
'.xml': 'application/xml',
}

function sendFile(res, file) {
res.writeHead(200, {
'content-type': types[path.extname(file)] || 'application/octet-stream',
'cache-control': 'no-store',
})
fs.createReadStream(file).pipe(res)
}

function serveStatic(req, res) {
const pathname = decodeURIComponent(new URL(req.url, `http://${req.headers.host}`).pathname)
const candidate = path.resolve(root, `.${pathname}`)
const file = candidate.startsWith(root) && fs.existsSync(candidate) && fs.statSync(candidate).isFile()
? candidate
: path.join(root, 'index.html')
sendFile(res, file)
}

function proxyApi(req, res) {
const upstreamUrl = new URL(req.url, apiTarget)
const client = upstreamUrl.protocol === 'https:' ? https : http
const upstream = client.request(upstreamUrl, {
method: req.method,
headers: { ...req.headers, host: upstreamUrl.host },
}, upstreamRes => {
const headers = { ...upstreamRes.headers }
delete headers.connection
delete headers['transfer-encoding']
res.writeHead(upstreamRes.statusCode || 502, headers)
upstreamRes.pipe(res)
})
upstream.on('error', error => {
res.writeHead(502, { 'content-type': 'text/plain' })
res.end(error.message)
})
req.pipe(upstream)
}

const server = http.createServer((req, res) => {
if (req.url.startsWith('/api/')) proxyApi(req, res)
else serveStatic(req, res)
})

server.on('upgrade', (req, socket, head) => {
if (!req.url.startsWith('/ws/')) return socket.destroy()
const upstreamPort = Number(wsTarget.port || (wsTarget.protocol === 'wss:' ? 443 : 80))
const writeUpgrade = () => {
upstream.write(`${req.method} ${req.url} HTTP/${req.httpVersion}\r\n`)
for (const [name, value] of Object.entries({ ...req.headers, host: wsTarget.host })) {
upstream.write(`${name}: ${value}\r\n`)
}
upstream.write('\r\n')
if (head.length) upstream.write(head)
socket.pipe(upstream).pipe(socket)
}
const upstream = wsTarget.protocol === 'wss:'
? tls.connect({ host: wsTarget.hostname, port: upstreamPort, servername: wsTarget.hostname }, writeUpgrade)
: net.connect({ host: wsTarget.hostname, port: upstreamPort }, writeUpgrade)
upstream.on('error', () => socket.destroy())
})

server.listen(port, host, () => {
console.log(`Bitfeed dev server: http://${host}:${port}`)
console.log(` /ws -> ${wsTarget.origin}`)
console.log(` /api -> ${apiTarget.origin}`)
})
Loading