feat(frontend): connect client to local SurrealDB

Add a SurrealDB service with a local IndexedDB-backed (indxdb://) connection for private, local-first data and an optional remote connection for sync. Initialize the local connection on app start and document the required VITE_* variables in .env.example.

Bump the client version to 0.0.1.
This commit is contained in:
Rafał Tyński
2026-10-03 20:23:18 +02:00
parent e8e1658b3a
commit 3b94358162
5 changed files with 148 additions and 4 deletions
+16
View File
@@ -0,0 +1,16 @@
# SurrealDB — frontend (Vite)
# Wspólna przestrzeń nazw / baza
VITE_SURREAL_NAMESPACE=hubmi
VITE_SURREAL_DATABASE=hubmi
# A) Lokalna baza w przeglądarce (WASM + IndexedDB; dane zostają lokalnie)
VITE_SURREAL_LOCAL_URL=indxdb://tarninahub
# B) Backend na dedykowanym porcie (tylko dla SurrealDB) — synchronizacja
VITE_SURREAL_REMOTE_URL=ws://127.0.0.1:8010/rpc
# Poświadczenia do zdalnej bazy. W produkcji NIE używaj root w przeglądarce —
# użyj tokenu dostępu rekordowego (record access) lub konta o wąskich uprawnieniach.
VITE_SURREAL_USERNAME=
VITE_SURREAL_PASSWORD=
+65 -3
View File
@@ -1,15 +1,17 @@
{
"name": "hubmi-client",
"version": "0.0.0",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "hubmi-client",
"version": "0.0.0",
"version": "0.0.1",
"dependencies": {
"@mdi/font": "^7.4.47",
"@surrealdb/wasm": "^2.6.1",
"@vueuse/core": "^15.0.0",
"surrealdb": "^2.0.10",
"vue": "^3.5.42",
"vuetify": "^4.2.3"
},
@@ -370,6 +372,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/@surrealdb/cbor": {
"version": "2.0.0-alpha.4",
"resolved": "https://registry.npmjs.org/@surrealdb/cbor/-/cbor-2.0.0-alpha.4.tgz",
"integrity": "sha512-l/hNZ3ZCOJq8rzwx1y5ZV/8b2bYCWdhGqRVY33Pu50NAHyAW5qLsMn3ZBkZow77mAKwNCsbDsRGnyYEuU95mjw==",
"license": "Apache-2.0",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@surrealdb/wasm": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/@surrealdb/wasm/-/wasm-2.6.1.tgz",
"integrity": "sha512-pBxgOX/wZ+aBvMIJbDv6UtUDCT0RjhpPqa31NMUmAZuTx9WfZb1PQan4vCBH9rcSPDR6BC8zJ4pFQUuliFQUEQ==",
"license": "Apache-2.0",
"peerDependencies": {
"surrealdb": "^2.0.0-0"
}
},
"node_modules/@types/node": {
"version": "24.19.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.1.tgz",
@@ -1147,6 +1167,33 @@
"node": ">=0.10.0"
}
},
"node_modules/surrealdb": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/surrealdb/-/surrealdb-2.0.10.tgz",
"integrity": "sha512-4r3LqhvGIRdEMUDO2u21jSi0C8x0x374mOmqCEEM5TrTsQWi5dUihBAfUFee+EBkrJeVDhBgcnvaMNPn+owc2Q==",
"license": "Apache-2.0",
"dependencies": {
"@surrealdb/sqon": "^0.1.5"
},
"peerDependencies": {
"tslib": "^2.6.3",
"typescript": "^5.0.0 || ^6.0.0"
}
},
"node_modules/surrealdb/node_modules/@surrealdb/sqon": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@surrealdb/sqon/-/sqon-0.1.5.tgz",
"integrity": "sha512-wTo5eREYb+spcErgyKsyNt/QVnz9MaPhOrhqalNp6BWWQNXqgd0gZ8Pub2jjckLrULxRi9Cujamif0T34LBhBA==",
"license": "Apache-2.0",
"dependencies": {
"@surrealdb/cbor": "2.0.0-alpha.4",
"uuidv7": "^1.0.1"
},
"peerDependencies": {
"tslib": "^2.6.3",
"typescript": "^5.0.0"
}
},
"node_modules/tinyglobby": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
@@ -1164,11 +1211,17 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD",
"peer": true
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -1185,6 +1238,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/uuidv7": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/uuidv7/-/uuidv7-1.2.1.tgz",
"integrity": "sha512-4kPkK3/XTQW9Hbm4CaqfICn+kY9LJtDVEOfgsRRra/+n2Ofg4NqzRFceAkxvQ/Ud/6BpHOPzj8cirqM7TzTN5Q==",
"license": "Apache-2.0",
"bin": {
"uuidv7": "cli.js"
}
},
"node_modules/vite": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.2.tgz",
+3 -1
View File
@@ -1,7 +1,7 @@
{
"name": "hubmi-client",
"private": true,
"version": "0.0.0",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
@@ -11,7 +11,9 @@
},
"dependencies": {
"@mdi/font": "^7.4.47",
"@surrealdb/wasm": "^2.6.1",
"@vueuse/core": "^15.0.0",
"surrealdb": "^2.0.10",
"vue": "^3.5.42",
"vuetify": "^4.2.3"
},
+5
View File
@@ -1,7 +1,12 @@
import { createApp } from 'vue'
import vuetify from './plugins/vuetify'
import { connectLocalSurreal } from './services/surreal'
import './style.css'
import './styles/a11y.css'
import App from './App.vue'
void connectLocalSurreal().catch((error: unknown) => {
console.error('Nie udało się połączyć z lokalną bazą SurrealDB', error)
})
createApp(App).use(vuetify).mount('#app')
+59
View File
@@ -0,0 +1,59 @@
import { Surreal, createRemoteEngines } from 'surrealdb'
import { createWasmEngines } from '@surrealdb/wasm'
const namespace = import.meta.env.VITE_SURREAL_NAMESPACE ?? 'hubmi'
const database = import.meta.env.VITE_SURREAL_DATABASE ?? 'hubmi'
/**
* A) Lokalna baza SurrealDB w przeglądarce.
* Persistencja przez IndexedDB (`indxdb://`) — localStorage jest zbyt mały.
* Dane prywatne użytkownika zostają tutaj i nie są wysyłane automatycznie.
*/
const localDatabase = import.meta.env.VITE_SURREAL_LOCAL_DATABASE ?? 'tarninahub'
const localUrl = import.meta.env.VITE_SURREAL_LOCAL_URL ?? `indxdb://${localDatabase}`
export const localSurreal = new Surreal({
engines: {
...createRemoteEngines(),
...createWasmEngines(),
},
})
let localConnection: Promise<void> | null = null
export function connectLocalSurreal(): Promise<void> {
localConnection ??= localSurreal
.connect(localUrl, { namespace, database })
.then(() => undefined)
return localConnection
}
/**
* B) Zdalna baza backendu na dedykowanym porcie (tylko dla SurrealDB),
* używana do synchronizacji danych uznanych za nadające się do wysłania.
*/
const remoteUrl =
import.meta.env.VITE_SURREAL_REMOTE_URL ?? 'ws://127.0.0.1:8010/rpc'
const username = import.meta.env.VITE_SURREAL_USERNAME as string | undefined
const password = import.meta.env.VITE_SURREAL_PASSWORD as string | undefined
export const remoteSurreal = new Surreal()
let remoteConnection: Promise<void> | null = null
export function connectRemoteSurreal(): Promise<void> {
remoteConnection ??= remoteSurreal
.connect(remoteUrl, {
namespace,
database,
...(username && password
? { authentication: { username, password } }
: {}),
})
.then(() => undefined)
return remoteConnection
}
export { namespace, database }