Skip to content
Draft
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
Binary file added bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"cm": "cz",
"lint": "eslint ./src/ --fix",
"server": "ts-node ./src/server.ts",
"server-bun": "bun ./src/server-bun.ts",
"prepare": "husky install",
"benchmark": "bash ./bench/run.sh",
"semantic-release": "semantic-release",
Expand Down Expand Up @@ -57,6 +58,7 @@
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"bun-types": "^0.5.7",
"conventional-changelog-conventionalcommits": "^5.0.0",
"dotenv": "^16.0.3",
"eslint": "^7.25.0",
Expand Down
120 changes: 120 additions & 0 deletions src/server-bun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { Redbase } from './redbase'

const port = parseInt(process.env['BROWSER_PORT'] || '3000')
const args = process.argv.slice(2)
const databaseName = args[0]
if (!databaseName) {
throw new Error('Provide a database name')
}
const db = new Redbase<unknown>(databaseName)

const ENTRY_PREFIX = '/entry/'
const PAGE_SIZE = 40

async function handleBrowse(url: URL) {
const host = `${url.protocol}//${url.host}`
const tagPath = url.pathname.slice(1)
const offset = parseInt(url.searchParams.get('offset') || '0')
const absolutePath = host + '/' + tagPath
const entries = await db.filter({
where: tagPath,
offset: offset,
limit: PAGE_SIZE,
ordering: 'desc',
})
const subTags = await db.tags({ where: tagPath })
const count = await db.count({ where: tagPath })

// Create actions for the UI
const actions: Record<string, string> = {
[`delete all ${count}`]: absolutePath + '?method=DELETE',
}
if (tagPath) {
actions['back to root'] = host
}
if (count > offset + PAGE_SIZE) {
actions[`next ${PAGE_SIZE}`] =
absolutePath + `?offset=${offset + PAGE_SIZE}`
actions[`last ${PAGE_SIZE}`] = absolutePath + `?offset=${count - PAGE_SIZE}`
}
if (offset > 0) {
actions[`prev ${PAGE_SIZE}`] =
absolutePath + `?offset=${Math.max(offset - PAGE_SIZE, 0)}`
}
for (const subTag of subTags) {
const tagParts = subTag.split('/')
const label = 'browse ' + decodeURIComponent(tagParts[tagParts.length - 1])
actions[label] = absolutePath + subTag
}

const json = {
total: count,
actions,
entries: entries.map(e => ({
'delete this': absolutePath + ENTRY_PREFIX + e.id + '?method=DELETE',
...e,
})),
}

return new Response(JSON.stringify(json), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}

async function handleDelete(url: URL) {
const tagPath = url.pathname.slice(1)
const entryPrefix = ENTRY_PREFIX.substring(1)
let header
if (tagPath.startsWith(entryPrefix)) {
// Deleting a single entry
await db.delete(tagPath.substring(entryPrefix.length))
header = { Location: '/' }
} else {
// Deleting many
await db.clear({ where: tagPath })
header = { Location: '/' + tagPath }
}
return new Response(undefined, {
status: 307,
headers: header,
})
}

const server = Bun.serve({
fetch(req: Request) {
const url = new URL(req.url || '')

const shouldDelete = url.searchParams.get('method') === 'DELETE'
console.log(
`Requesting ${shouldDelete ? 'delete' : 'browse'} on path: ${
url.pathname
}`
)

if (shouldDelete) {
return handleDelete(url)
} else {
return handleBrowse(url)
}
},

// baseURI: "http://localhost:3000",

// this is called when fetch() throws or rejects
// error(err: Error) {
// return new Response("uh oh! :(\n" + err.toString(), { status: 500 });
// },

// this boolean enables bun's default error handler
development: false,
// note: this isn't node, but for compatibility bun supports process.env + more stuff in process

// SSL is enabled if these two are set
// certFile: './cert.pem',
// keyFile: './key.pem',

port, // number or string
})

console.log(`Listening on http://localhost:${server.port}...`)
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
"types": [
"bun-types"
] /* Type declaration files to be included in compilation. */,
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
"resolveJsonModule": true /* Include modules imported with '.json' extension */,
Expand Down