mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-17 21:18:47 +03:00
Make schemas work with postgres
This commit is contained in:
@@ -45,6 +45,7 @@ addDefaults(config, {
|
||||
},
|
||||
userCounterURL: null,
|
||||
youtubeAPIKey: null,
|
||||
postgres: null
|
||||
});
|
||||
|
||||
// Add defaults
|
||||
|
||||
@@ -4,27 +4,25 @@ import { Pool } from 'pg';
|
||||
|
||||
import fs from "fs";
|
||||
|
||||
export class Mysql implements IDatabase {
|
||||
export class Postgres implements IDatabase {
|
||||
private pool: Pool;
|
||||
|
||||
constructor(private config: any) {}
|
||||
|
||||
init(): void {
|
||||
this.pool = new Pool();
|
||||
async init(): Promise<void> {
|
||||
this.pool = new Pool(this.config.postgres);
|
||||
|
||||
if (!this.config.readOnly) {
|
||||
// Upgrade database if required
|
||||
this.upgradeDB(this.config.fileNamePrefix, this.config.dbSchemaFolder);
|
||||
|
||||
if (this.config.createDbIfNotExists && !this.config.readOnly && fs.existsSync(this.config.dbSchemaFileName)) {
|
||||
this.pool.query(this.processUpgradeQuery(fs.readFileSync(this.config.dbSchemaFileName).toString()));
|
||||
await this.pool.query(this.processUpgradeQuery(fs.readFileSync(this.config.dbSchemaFileName).toString()));
|
||||
}
|
||||
|
||||
// Upgrade database if required
|
||||
await this.upgradeDB(this.config.fileNamePrefix, this.config.dbSchemaFolder);
|
||||
}
|
||||
}
|
||||
|
||||
async prepare(type: QueryType, query: string, params?: any[]) {
|
||||
Logger.debug(`prepare (postgres): type: ${type}, query: ${query}, params: ${params}`);
|
||||
|
||||
// Convert query to use numbered parameters
|
||||
let count = 1;
|
||||
for (let char = 0; char < query.length; char++) {
|
||||
@@ -34,7 +32,9 @@ export class Mysql implements IDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
const queryResult = await this.pool.query(query, params);
|
||||
Logger.debug(`prepare (postgres): type: ${type}, query: ${query}, params: ${params}`);
|
||||
|
||||
const queryResult = await this.pool.query({text: query, values: params});
|
||||
|
||||
switch (type) {
|
||||
case 'get': {
|
||||
@@ -51,7 +51,7 @@ export class Mysql implements IDatabase {
|
||||
|
||||
private async upgradeDB(fileNamePrefix: string, schemaFolder: string) {
|
||||
const versionCodeInfo = await this.pool.query("SELECT value FROM config WHERE key = 'version'");
|
||||
let versionCode = versionCodeInfo ? versionCodeInfo.rows[0].value : 0;
|
||||
let versionCode = versionCodeInfo.rows[0] ? versionCodeInfo.rows[0].value : 0;
|
||||
|
||||
let path = schemaFolder + "/_upgrade_" + fileNamePrefix + "_" + (parseInt(versionCode) + 1) + ".sql";
|
||||
Logger.debug('db update: trying ' + path);
|
||||
@@ -67,7 +67,9 @@ export class Mysql implements IDatabase {
|
||||
}
|
||||
|
||||
private processUpgradeQuery(query: string): string {
|
||||
let result = query.replace(/sha256\((.*?)\)/gm, "digest($1, 'sha256')");
|
||||
let result = query.toLocaleLowerCase();
|
||||
result = result.replace(/sha256\((.*?)\)/gm, "digest($1, 'sha256')");
|
||||
result = result.replace(/integer/gm, "numeric");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {config} from '../config';
|
||||
import {Sqlite} from './Sqlite';
|
||||
import {Mysql} from './Mysql';
|
||||
import {Postgres} from './Postgres';
|
||||
import {IDatabase} from './IDatabase';
|
||||
|
||||
|
||||
@@ -9,6 +10,26 @@ let privateDB: IDatabase;
|
||||
if (config.mysql) {
|
||||
db = new Mysql(config.mysql);
|
||||
privateDB = new Mysql(config.privateMysql);
|
||||
} else if (config.postgres) {
|
||||
db = new Postgres({
|
||||
dbSchemaFileName: config.dbSchema,
|
||||
dbSchemaFolder: config.schemaFolder,
|
||||
fileNamePrefix: 'sponsorTimes',
|
||||
readOnly: config.readOnly,
|
||||
createDbIfNotExists: config.createDatabaseIfNotExist,
|
||||
enableWalCheckpointNumber: !config.readOnly && config.mode === "production",
|
||||
postgres: config.postgres
|
||||
});
|
||||
|
||||
privateDB = new Sqlite({
|
||||
dbPath: config.privateDB,
|
||||
dbSchemaFileName: config.privateDBSchema,
|
||||
dbSchemaFolder: config.schemaFolder,
|
||||
fileNamePrefix: 'private',
|
||||
readOnly: config.readOnly,
|
||||
createDbIfNotExists: config.createDatabaseIfNotExist,
|
||||
enableWalCheckpointNumber: false
|
||||
});
|
||||
} else {
|
||||
db = new Sqlite({
|
||||
dbPath: config.db,
|
||||
|
||||
@@ -52,11 +52,14 @@ async function getSegmentsByVideoID(req: Request, videoID: string, categories: C
|
||||
const segments: Segment[] = [];
|
||||
|
||||
try {
|
||||
categories.filter((category) => !/[^a-z|_|-]/.test(category));
|
||||
|
||||
const segmentsByCategory: SBRecord<Category, DBSegment[]> = (await db
|
||||
.prepare(
|
||||
'all',
|
||||
`SELECT startTime, endTime, votes, locked, UUID, category, shadowHidden FROM sponsorTimes WHERE videoID = ? AND category IN (${Array(categories.length).fill('?').join()}) ORDER BY startTime`,
|
||||
[videoID, categories]
|
||||
`SELECT startTime, endTime, votes, locked, UUID, category, shadowHidden FROM sponsorTimes
|
||||
WHERE videoID = ? AND category IN (${categories.map((c) => "'" + c + "'")}) ORDER BY startTime`,
|
||||
[videoID]
|
||||
)).reduce((acc: SBRecord<Category, DBSegment[]>, segment: DBSegment) => {
|
||||
acc[segment.category] = acc[segment.category] || [];
|
||||
acc[segment.category].push(segment);
|
||||
@@ -64,10 +67,15 @@ async function getSegmentsByVideoID(req: Request, videoID: string, categories: C
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
console.log(segmentsByCategory)
|
||||
|
||||
|
||||
for (const [category, categorySegments] of Object.entries(segmentsByCategory)) {
|
||||
segments.push(...(await prepareCategorySegments(req, videoID as VideoID, category as Category, categorySegments, cache)));
|
||||
}
|
||||
|
||||
console.log(segments)
|
||||
|
||||
return segments;
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
@@ -84,11 +92,14 @@ async function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash,
|
||||
try {
|
||||
type SegmentWithHashPerVideoID = SBRecord<VideoID, {hash: VideoIDHash, segmentPerCategory: SBRecord<Category, DBSegment[]>}>;
|
||||
|
||||
categories.filter((category) => !/[^a-z|_|-]/.test(category));
|
||||
|
||||
const segmentPerVideoID: SegmentWithHashPerVideoID = (await db
|
||||
.prepare(
|
||||
'all',
|
||||
`SELECT videoID, startTime, endTime, votes, locked, UUID, category, shadowHidden, hashedVideoID FROM sponsorTimes WHERE hashedVideoID LIKE ? AND category IN (${Array(categories.length).fill('?').join()}) ORDER BY startTime`,
|
||||
[hashedVideoIDPrefix + '%', categories]
|
||||
`SELECT videoID, startTime, endTime, votes, locked, UUID, category, shadowHidden, hashedVideoID FROM sponsorTimes
|
||||
WHERE hashedVideoID LIKE ? AND category IN (${categories.map((c) => "'" + c + "'")}) ORDER BY startTime`,
|
||||
[hashedVideoIDPrefix + '%']
|
||||
)).reduce((acc: SegmentWithHashPerVideoID, segment: DBSegment) => {
|
||||
acc[segment.videoID] = acc[segment.videoID] || {
|
||||
hash: segment.hashedVideoID,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PoolConfig } from 'pg';
|
||||
import * as redis from 'redis';
|
||||
|
||||
export interface SBSConfig {
|
||||
@@ -36,6 +37,7 @@ export interface SBSConfig {
|
||||
minimumPrefix?: string;
|
||||
maximumPrefix?: string;
|
||||
redis?: redis.ClientOpts;
|
||||
postgres?: PoolConfig;
|
||||
}
|
||||
|
||||
export interface WebhookConfig {
|
||||
@@ -50,3 +52,13 @@ export interface RateLimitConfig {
|
||||
message: string;
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
export interface PostgresConfig {
|
||||
dbSchemaFileName: string;
|
||||
dbSchemaFolder: string;
|
||||
fileNamePrefix: string;
|
||||
readOnly: boolean;
|
||||
createDbIfNotExists: boolean;
|
||||
enableWalCheckpointNumber: boolean;
|
||||
postgres: PoolConfig;
|
||||
}
|
||||
Reference in New Issue
Block a user