From 274b49dd27c9429dc5dc71582bbef69dd843fc72 Mon Sep 17 00:00:00 2001 From: qwerinope Date: Thu, 26 Jun 2025 01:17:45 +0200 Subject: [PATCH] change databases from surrealdb to pocketbase --- .example.env | 8 +--- .gitignore | 3 +- bot/auth.ts | 2 +- bot/db/connection.ts | 52 +++++++++++------------- bot/db/dbAuth.ts | 93 +++++++++++++------------------------------ bun.lock | 12 ++---- compose.yml | 9 +++++ package.json | 2 +- pocketbase/Dockerfile | 20 ++++++++++ 9 files changed, 89 insertions(+), 112 deletions(-) create mode 100644 pocketbase/Dockerfile diff --git a/.example.env b/.example.env index 46d0942..dd862a7 100644 --- a/.example.env +++ b/.example.env @@ -21,9 +21,5 @@ STREAMER_ID= # Twitch ID of the streaming user CHATTER_ID= # Twitch ID of the chatting user CHATTER_IS_STREAMER= # If the bot that activates on commands is on the same account as the streamer, set this to true. Make sure the STREAMER_ID and CHATTER_ID match in that case. -# SurrealDB config -SURREAL_URL= # SurrealDB URL, can either be remotely hosted or selfhosted -SURREAL_NAMESPACE= # SurrealDB Namespace. You need to create this manually -SURREAL_DATABASE= # SurrealDB Database. You need to create this manually -SURREAL_USERNAME= # SurrealDB username for authenticating -SURREAL_PASSWORD= # SurrealDB password for authenticating. Remember to escape characters like $ with a backslash +# Pocketbase config +# POCKETBASE_URL= # Pocketbase URL. Defaults to localhost:8090 diff --git a/.gitignore b/.gitignore index 902ca7e..9607f6b 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,6 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Finder (MacOS) folder config .DS_Store -# Redis/Valkey database +# Redis/Valkey & Pocketbase databases db/redis/ +db/pocketbase/ diff --git a/bot/auth.ts b/bot/auth.ts index f714777..ce754d8 100644 --- a/bot/auth.ts +++ b/bot/auth.ts @@ -66,7 +66,7 @@ export async function createAuthProvider(user: string, intents: string[], stream authData.onRefresh(async (user, token) => { console.info(`Successfully refreshed auth for user ${user}`); - await updateAuthRecord(user, token.scope, token); + await updateAuthRecord(user, token); }); authData.onRefreshFailure((user, err) => { diff --git a/bot/db/connection.ts b/bot/db/connection.ts index 48b8e7f..e92e547 100644 --- a/bot/db/connection.ts +++ b/bot/db/connection.ts @@ -1,31 +1,27 @@ -import Surreal from "surrealdb"; +import type { AccessToken } from "@twurple/auth"; +import PocketBase, { RecordService } from "pocketbase"; -const surrealurl = process.env.SURREAL_URL ?? ""; -if (surrealurl === "") { console.error("Please provide a SURREAL_URL in .env."); process.exit(1); }; -const namespace = process.env.SURREAL_NAMESPACE ?? ""; -if (namespace === "") { console.error("Please provide a SURREAL_NAMESPACE in .env."); process.exit(1); }; -const database = process.env.SURREAL_DATABASE ?? ""; -if (database === "") { console.error("Please provide a SURREAL_DATABASE in .env."); process.exit(1); }; -const username = process.env.SURREAL_USERNAME ?? ""; -if (username === "") { console.error("Please provide a SURREAL_USERNAME in .env."); process.exit(1); }; -const password = process.env.SURREAL_PASSWORD ?? ""; -if (password === "") { console.error("Please provide a SURREAL_PASSWORD in .env."); process.exit(1); }; +const pocketbaseurl = process.env.POCKETBASE_URL ?? "localhost:8090"; +if (pocketbaseurl === "") { console.error("Please provide a POCKETBASE_URL in .env."); process.exit(1); }; -export default async function DB(): Promise { - const db = new Surreal(); - try { - await db.connect(surrealurl, { - auth: { - username, - password - } - }); - await db.use({ namespace, database }); - return db; - } - catch (err) { - console.error("Failed to connect to SurrealDB:", err instanceof Error ? err.message : String(err)); - await db.close(); - throw err; - }; +export type authRecord = { + id: string; + accesstoken: AccessToken; }; + +export type userRecord = { + id: string; + username: string; + balance: number; + inventory: object; + lastlootbox: string; +}; + +type TypedPocketBase = { + collection(idOrName: 'auth'): RecordService; + collection(idOrName: 'users'): RecordService; +}; + +const pb = new PocketBase(pocketbaseurl) as TypedPocketBase; + +export default pb; diff --git a/bot/db/dbAuth.ts b/bot/db/dbAuth.ts index e74e320..1242f68 100644 --- a/bot/db/dbAuth.ts +++ b/bot/db/dbAuth.ts @@ -1,77 +1,38 @@ import type { AccessToken } from "@twurple/auth"; -import DB from "./connection"; -import type { RecordId } from "surrealdb"; - -type authRecord = { - accesstoken: AccessToken, - user: string, -}; - -export async function createAuthRecord(token: AccessToken, userId: string): Promise { - const db = await DB(); - if (!db) return; - - const data: authRecord = { - accesstoken: token, - user: userId - }; +import pocketbase, { type authRecord } from "./connection"; +const pb = pocketbase.collection('auth'); +export async function createAuthRecord(token: AccessToken, userId: string) { try { - await db.create("auth", data); - } catch (err) { - console.error(err); - } finally { - await db.close(); - }; -}; - -type getAuthRecordQuery = authRecord & { id: RecordId }; -type getAuthRecordResult = { accesstoken: AccessToken, id: RecordId }; - -export async function getAuthRecord(userId: string, requiredIntents: string[]): Promise { - const db = await DB(); - if (!db) return undefined; - try { - const data = await db.query("SELECT * from auth WHERE user=$userId AND accesstoken.scope CONTAINSALL $intents;", { userId, intents: requiredIntents }); - if (!data[0] || !data[0][0]) return undefined; - return { accesstoken: data[0][0].accesstoken, id: data[0][0].id }; - } catch (err) { - console.error(err); - return undefined; - } finally { - await db.close(); - }; -}; - -export async function updateAuthRecord(userId: string, intents: string[], newtoken: AccessToken): Promise { - const db = await DB(); - if (!db) return false; - try { - const data = await getAuthRecord(userId, intents); - const newrecord: authRecord = { - accesstoken: newtoken, - user: userId + const data: authRecord = { + accesstoken: token, + id: userId }; - await db.update(data?.id!, newrecord); - return true; + await pb.create(data); + } catch (err) { }; +}; + +export async function getAuthRecord(userId: string, requiredIntents: string[]) { + try { + const data = await pb.getOne(userId); + if (!requiredIntents.every(intent => data.accesstoken.scope.includes(intent))) return undefined; + return { accesstoken: data.accesstoken }; } catch (err) { - console.error(err); - return false; - } finally { - await db.close(); + return undefined; }; }; +export async function updateAuthRecord(userId: string, newtoken: AccessToken) { + try { + const newrecord = { + accesstoken: newtoken, + }; + await pb.update(userId, newrecord); + } catch (err) { }; +}; + export async function deleteAuthRecord(userId: string): Promise { - const db = await DB(); - if (!db) return; try { - const data = await db.query("SELECT * FROM auth WHERE user=$userId;", { userId }); - if (!data[0] || !data[0][0]) return undefined; - for (const obj of data[0]) { - db.delete(obj.id); - }; - } catch (err) { - console.error(err); - }; + await pb.delete(userId); + } catch (err) { }; }; diff --git a/bun.lock b/bun.lock index 72432fe..ab7c501 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "dependencies": { "@twurple/auth": "^7.3.0", "@twurple/eventsub-http": "^7.3.0", - "surrealdb": "^1.3.2", + "pocketbase": "^0.26.1", }, "devDependencies": { "@types/bun": "latest", @@ -75,12 +75,12 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "isows": ["isows@1.0.7", "", { "peerDependencies": { "ws": "*" } }, "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg=="], - "klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "pocketbase": ["pocketbase@0.26.1", "", {}, "sha512-fjcPDpxyqTZCwqGUTPUV7vssIsNMqHxk9GxbhxYHPEf18RqX2d9cpSqbbHk7aas30jqkgptuKfG7aY/Mytjj3g=="], + "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], @@ -89,8 +89,6 @@ "statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="], - "surrealdb": ["surrealdb@1.3.2", "", { "dependencies": { "isows": "^1.0.6", "uuidv7": "^1.0.1" }, "peerDependencies": { "tslib": "^2.6.3", "typescript": "^5.0.0" } }, "sha512-mL7nij33iuon3IQP72F46fgX3p2LAxFCWCBDbZB7IohZ13RTEwJVNq7nZeP1eMSceQUpKzS6OHIWOuF9LYAkNw=="], - "toidentifier": ["toidentifier@1.0.0", "", {}, "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="], "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], @@ -103,14 +101,10 @@ "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - "uuidv7": ["uuidv7@1.0.2", "", { "bin": { "uuidv7": "cli.js" } }, "sha512-8JQkH4ooXnm1JCIhqTMbtmdnYEn6oKukBxHn1Ic9878jMkL7daTI7anTExfY18VRCX7tcdn5quzvCb6EWrR8PA=="], - "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], - "httpanda/@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="], } } diff --git a/compose.yml b/compose.yml index c0e3ae8..be1c89c 100644 --- a/compose.yml +++ b/compose.yml @@ -9,3 +9,12 @@ services: - ./db/redis:/data environment: - VALKEY_EXTRA_FLAGS=--save 60 1 + pocketbase: + container_name: qweribot-pocketbase + build: + context: ./pocketbase + ports: + - 8090:8090 + restart: no + volumes: + - ./db/pocketbase:/pb/pb_data diff --git a/package.json b/package.json index 894c77c..986a8b8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,6 @@ "dependencies": { "@twurple/auth": "^7.3.0", "@twurple/eventsub-http": "^7.3.0", - "surrealdb": "^1.3.2" + "pocketbase": "^0.26.1" } } diff --git a/pocketbase/Dockerfile b/pocketbase/Dockerfile new file mode 100644 index 0000000..945cee3 --- /dev/null +++ b/pocketbase/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:latest + +ARG PB_VERSION=0.28.4 +ARG PB_ZIPNAME=pocketbase_${PB_VERSION}_linux_amd64.zip + +RUN apk add --no-cache \ + unzip \ + ca-certificates + +ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/${PB_ZIPNAME} /tmp/${PB_ZIPNAME} +ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/checksums.txt /tmp/checksums.txt +WORKDIR /tmp +RUN grep ${PB_ZIPNAME} checksums.txt | sha256sum -c +RUN unzip /tmp/${PB_ZIPNAME} -d /pb/ + +COPY ./pb_migrations /pb/pb_migrations +COPY ./pb_hooks /pb/pb_hooks + +EXPOSE 8090 +CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8090"]