icy3dw/src/server/object/persistence.ts

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),
[],
);
}
}