mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-19 04:51:38 +01:00
change databases from surrealdb to pocketbase
This commit is contained in:
@@ -21,9 +21,5 @@ STREAMER_ID= # Twitch ID of the streaming user
|
|||||||
CHATTER_ID= # Twitch ID of the chatting 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.
|
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
|
# Pocketbase config
|
||||||
SURREAL_URL= # SurrealDB URL, can either be remotely hosted or selfhosted
|
# POCKETBASE_URL= # Pocketbase URL. Defaults to localhost:8090
|
||||||
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
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -33,5 +33,6 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|||||||
# Finder (MacOS) folder config
|
# Finder (MacOS) folder config
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Redis/Valkey database
|
# Redis/Valkey & Pocketbase databases
|
||||||
db/redis/
|
db/redis/
|
||||||
|
db/pocketbase/
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export async function createAuthProvider(user: string, intents: string[], stream
|
|||||||
|
|
||||||
authData.onRefresh(async (user, token) => {
|
authData.onRefresh(async (user, token) => {
|
||||||
console.info(`Successfully refreshed auth for user ${user}`);
|
console.info(`Successfully refreshed auth for user ${user}`);
|
||||||
await updateAuthRecord(user, token.scope, token);
|
await updateAuthRecord(user, token);
|
||||||
});
|
});
|
||||||
|
|
||||||
authData.onRefreshFailure((user, err) => {
|
authData.onRefreshFailure((user, err) => {
|
||||||
|
|||||||
@@ -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 ?? "";
|
const pocketbaseurl = process.env.POCKETBASE_URL ?? "localhost:8090";
|
||||||
if (surrealurl === "") { console.error("Please provide a SURREAL_URL in .env."); process.exit(1); };
|
if (pocketbaseurl === "") { console.error("Please provide a POCKETBASE_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); };
|
|
||||||
|
|
||||||
export default async function DB(): Promise<Surreal> {
|
export type authRecord = {
|
||||||
const db = new Surreal();
|
id: string;
|
||||||
try {
|
accesstoken: AccessToken;
|
||||||
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 userRecord = {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
balance: number;
|
||||||
|
inventory: object;
|
||||||
|
lastlootbox: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TypedPocketBase = {
|
||||||
|
collection(idOrName: 'auth'): RecordService<authRecord>;
|
||||||
|
collection(idOrName: 'users'): RecordService<userRecord>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb = new PocketBase(pocketbaseurl) as TypedPocketBase;
|
||||||
|
|
||||||
|
export default pb;
|
||||||
|
|||||||
@@ -1,77 +1,38 @@
|
|||||||
import type { AccessToken } from "@twurple/auth";
|
import type { AccessToken } from "@twurple/auth";
|
||||||
import DB from "./connection";
|
import pocketbase, { type authRecord } from "./connection";
|
||||||
import type { RecordId } from "surrealdb";
|
const pb = pocketbase.collection('auth');
|
||||||
|
|
||||||
type authRecord = {
|
|
||||||
accesstoken: AccessToken,
|
|
||||||
user: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function createAuthRecord(token: AccessToken, userId: string): Promise<void> {
|
|
||||||
const db = await DB();
|
|
||||||
if (!db) return;
|
|
||||||
|
|
||||||
const data: authRecord = {
|
|
||||||
accesstoken: token,
|
|
||||||
user: userId
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export async function createAuthRecord(token: AccessToken, userId: string) {
|
||||||
try {
|
try {
|
||||||
await db.create("auth", data);
|
const data: authRecord = {
|
||||||
} catch (err) {
|
accesstoken: token,
|
||||||
console.error(err);
|
id: userId
|
||||||
} finally {
|
|
||||||
await db.close();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type getAuthRecordQuery = authRecord & { id: RecordId };
|
|
||||||
type getAuthRecordResult = { accesstoken: AccessToken, id: RecordId };
|
|
||||||
|
|
||||||
export async function getAuthRecord(userId: string, requiredIntents: string[]): Promise<getAuthRecordResult | undefined> {
|
|
||||||
const db = await DB();
|
|
||||||
if (!db) return undefined;
|
|
||||||
try {
|
|
||||||
const data = await db.query<getAuthRecordQuery[][]>("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<boolean> {
|
|
||||||
const db = await DB();
|
|
||||||
if (!db) return false;
|
|
||||||
try {
|
|
||||||
const data = await getAuthRecord(userId, intents);
|
|
||||||
const newrecord: authRecord = {
|
|
||||||
accesstoken: newtoken,
|
|
||||||
user: userId
|
|
||||||
};
|
};
|
||||||
await db.update(data?.id!, newrecord);
|
await pb.create(data);
|
||||||
return true;
|
} 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) {
|
} catch (err) {
|
||||||
console.error(err);
|
return undefined;
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
await db.close();
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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<void> {
|
export async function deleteAuthRecord(userId: string): Promise<void> {
|
||||||
const db = await DB();
|
|
||||||
if (!db) return;
|
|
||||||
try {
|
try {
|
||||||
const data = await db.query<getAuthRecordQuery[][]>("SELECT * FROM auth WHERE user=$userId;", { userId });
|
await pb.delete(userId);
|
||||||
if (!data[0] || !data[0][0]) return undefined;
|
} catch (err) { };
|
||||||
for (const obj of data[0]) {
|
|
||||||
db.delete(obj.id);
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
12
bun.lock
12
bun.lock
@@ -6,7 +6,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@twurple/auth": "^7.3.0",
|
"@twurple/auth": "^7.3.0",
|
||||||
"@twurple/eventsub-http": "^7.3.0",
|
"@twurple/eventsub-http": "^7.3.0",
|
||||||
"surrealdb": "^1.3.2",
|
"pocketbase": "^0.26.1",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
@@ -75,12 +75,12 @@
|
|||||||
|
|
||||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
||||||
|
|
||||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||||
@@ -89,8 +89,6 @@
|
|||||||
|
|
||||||
"statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
|
"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=="],
|
"toidentifier": ["toidentifier@1.0.0", "", {}, "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="],
|
||||||
|
|
||||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
@@ -103,14 +101,10 @@
|
|||||||
|
|
||||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
"httpanda/@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,3 +9,12 @@ services:
|
|||||||
- ./db/redis:/data
|
- ./db/redis:/data
|
||||||
environment:
|
environment:
|
||||||
- VALKEY_EXTRA_FLAGS=--save 60 1
|
- 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
|
||||||
|
|||||||
@@ -12,6 +12,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@twurple/auth": "^7.3.0",
|
"@twurple/auth": "^7.3.0",
|
||||||
"@twurple/eventsub-http": "^7.3.0",
|
"@twurple/eventsub-http": "^7.3.0",
|
||||||
"surrealdb": "^1.3.2"
|
"pocketbase": "^0.26.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
pocketbase/Dockerfile
Normal file
20
pocketbase/Dockerfile
Normal file
@@ -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"]
|
||||||
Reference in New Issue
Block a user