140 lines
3.8 KiB
TypeScript
140 lines
3.8 KiB
TypeScript
import { join } from 'path';
|
|
import sqlite3 from 'sqlite3';
|
|
import { open, Database } from 'sqlite';
|
|
import { IcyNetUser } from '../../common/types/user';
|
|
import { DatabasePony, DatabaseUser } from '../types/database';
|
|
|
|
export class Persistence {
|
|
private db!: Database;
|
|
|
|
constructor(private _store = join(process.cwd(), 'ponies.db')) {}
|
|
|
|
async initialize(): Promise<void> {
|
|
this.db = await open({
|
|
filename: this._store,
|
|
driver: sqlite3.cached.Database,
|
|
});
|
|
|
|
await this.db.migrate();
|
|
}
|
|
|
|
async upsertUser(user: IcyNetUser): Promise<DatabaseUser> {
|
|
const existing = await this.db.get<DatabaseUser>(
|
|
`SELECT * FROM User WHERE id = ?`,
|
|
user.id,
|
|
);
|
|
if (existing) {
|
|
if (existing.display_name !== user.display_name) {
|
|
existing.display_name = user.display_name;
|
|
await this.db.run(`UPDATE User SET display_name = ? WHERE id = ?`, [
|
|
user.display_name,
|
|
user.id,
|
|
]);
|
|
}
|
|
return existing;
|
|
}
|
|
|
|
const time = new Date().toISOString();
|
|
await this.db.run(
|
|
`INSERT INTO User (id, username, display_name, created_at) VALUES (?,?,?,?)`,
|
|
[user.id, user.username, user.display_name, time],
|
|
);
|
|
return { ...user, created_at: time };
|
|
}
|
|
|
|
async upsertPony(
|
|
user: IcyNetUser,
|
|
data: DatabasePony,
|
|
): Promise<DatabasePony> {
|
|
const pony = await this.db.get<DatabasePony>(
|
|
'SELECT * FROM Pony WHERE user_id = ?',
|
|
[user.id],
|
|
);
|
|
|
|
const updateData = this.serializePony({ ...pony, ...data });
|
|
|
|
if (pony) {
|
|
await this.db.run(
|
|
`UPDATE Pony SET display_name = ?, position = ?, rotation = ?, character = ? WHERE user_id = ?`,
|
|
[
|
|
user.display_name,
|
|
updateData.position,
|
|
updateData.rotation,
|
|
updateData.character,
|
|
user.id,
|
|
],
|
|
);
|
|
return this.deserializePony(updateData);
|
|
}
|
|
|
|
updateData.created_at = new Date().toISOString();
|
|
updateData.user_id = user.id;
|
|
|
|
const keys = this.filterPonyKeys(updateData);
|
|
const insert = Array.from({ length: keys.length }, () => '?').join(',');
|
|
|
|
await this.db.run(
|
|
`INSERT INTO Pony (${keys.join(',')}) VALUES (${insert})`,
|
|
keys.map((key) => (updateData as any)[key]),
|
|
);
|
|
|
|
return this.deserializePony(updateData);
|
|
}
|
|
|
|
async getUserPony(user: IcyNetUser): Promise<DatabasePony | null> {
|
|
const result = await this.db.get<DatabasePony>(
|
|
'SELECT * FROM Pony WHERE user_id = ?',
|
|
user.id,
|
|
);
|
|
|
|
return result ? this.deserializePony(result) : null;
|
|
}
|
|
|
|
private serializePony(data: DatabasePony): DatabasePony {
|
|
return {
|
|
...data,
|
|
position: Array.isArray(data.position)
|
|
? JSON.stringify(data.position)
|
|
: data.position,
|
|
rotation: Array.isArray(data.rotation)
|
|
? JSON.stringify(data.rotation)
|
|
: data.rotation,
|
|
character:
|
|
typeof data.character !== 'string'
|
|
? JSON.stringify(data.character)
|
|
: data.character,
|
|
};
|
|
}
|
|
|
|
private deserializePony(data: DatabasePony): DatabasePony {
|
|
return {
|
|
...data,
|
|
position: Array.isArray(data.position)
|
|
? data.position
|
|
: JSON.parse(data.position as string),
|
|
rotation: Array.isArray(data.rotation)
|
|
? data.rotation
|
|
: JSON.parse(data.rotation as string),
|
|
character:
|
|
typeof data.character !== 'string'
|
|
? data.character
|
|
: JSON.parse(data.character as string),
|
|
};
|
|
}
|
|
|
|
private filterPonyKeys(data: DatabasePony): string[] {
|
|
const keys = [
|
|
'display_name',
|
|
'position',
|
|
'rotation',
|
|
'character',
|
|
'created_at',
|
|
'user_id',
|
|
];
|
|
return Object.keys(data).reduce<string[]>(
|
|
(list, current) => (keys.includes(current) ? [...list, current] : list),
|
|
[],
|
|
);
|
|
}
|
|
}
|