diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..3c86c66 --- /dev/null +++ b/client/.env.example @@ -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= diff --git a/client/package-lock.json b/client/package-lock.json index 342554f..38a7685 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -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", diff --git a/client/package.json b/client/package.json index d40edb1..534cf08 100644 --- a/client/package.json +++ b/client/package.json @@ -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" }, diff --git a/client/src/main.ts b/client/src/main.ts index 59e8969..b0a8e28 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -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') diff --git a/client/src/services/surreal.ts b/client/src/services/surreal.ts new file mode 100644 index 0000000..af735db --- /dev/null +++ b/client/src/services/surreal.ts @@ -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 | null = null + +export function connectLocalSurreal(): Promise { + 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 | null = null + +export function connectRemoteSurreal(): Promise { + remoteConnection ??= remoteSurreal + .connect(remoteUrl, { + namespace, + database, + ...(username && password + ? { authentication: { username, password } } + : {}), + }) + .then(() => undefined) + + return remoteConnection +} + +export { namespace, database }