mirror of https://github.com/tycrek/ass
parent
fa322e4166
commit
a8f1d55078
@ -0,0 +1,238 @@
|
||||
import { AssFile, AssUser, MongoDBConfiguration, NID, UploadToken } from 'ass';
|
||||
import { UserConfig } from '../UserConfig';
|
||||
import { Database, DatabaseTable, DatabaseValue } from './database';
|
||||
import mongoose, { Model, Mongoose, Schema } from 'mongoose';
|
||||
import { log } from '../log';
|
||||
|
||||
interface TableVersion {
|
||||
name: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
const VERSIONS_SCHEMA = new Schema<TableVersion>({
|
||||
name: String,
|
||||
version: Number
|
||||
});
|
||||
|
||||
interface MongoSchema<T> {
|
||||
id: NID,
|
||||
data: T
|
||||
}
|
||||
|
||||
const FILE_SCHEMA = new Schema<MongoSchema<AssFile>>({
|
||||
id: String,
|
||||
data: {
|
||||
fakeid: String,
|
||||
fileKey: String,
|
||||
filename: String,
|
||||
mimetype: String,
|
||||
save: {
|
||||
local: String,
|
||||
s3: Boolean // this will break if it gets the url object
|
||||
// but im so fucking tired of this, were just
|
||||
// going to keep it like this until it becomes
|
||||
// a problem
|
||||
},
|
||||
sha256: String,
|
||||
size: Number,
|
||||
timestamp: String,
|
||||
uploader: String
|
||||
}
|
||||
});
|
||||
|
||||
const TOKEN_SCHEMA = new Schema<MongoSchema<UploadToken>>({
|
||||
id: String,
|
||||
data: {
|
||||
id: String,
|
||||
token: String,
|
||||
hint: String
|
||||
}
|
||||
});
|
||||
|
||||
const USER_SCHEMA = new Schema<MongoSchema<AssUser>>({
|
||||
id: String,
|
||||
data: {
|
||||
id: String,
|
||||
username: String,
|
||||
password: String,
|
||||
admin: Boolean,
|
||||
tokens: [ String ],
|
||||
files: [ String ],
|
||||
meta: {
|
||||
type: String,
|
||||
get: (v: string) => JSON.parse(v),
|
||||
set: (v: string) => JSON.stringify(v)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* database adapter for mongodb
|
||||
*/
|
||||
export class MongoDBDatabase implements Database {
|
||||
private _client: Mongoose;
|
||||
|
||||
// mongoose models
|
||||
private _versionModel: Model<TableVersion>;
|
||||
private _fileModel: Model<MongoSchema<AssFile>>;
|
||||
private _tokenModel: Model<MongoSchema<UploadToken>>;
|
||||
private _userModel: Model<MongoSchema<AssUser>>;
|
||||
|
||||
private _validateConfig(): string | undefined {
|
||||
// make sure the configuration exists
|
||||
if (!UserConfig.ready) return 'User configuration not ready';
|
||||
if (typeof UserConfig.config.database != 'object') return 'MongoDB configuration missing';
|
||||
if (UserConfig.config.database.kind != 'mongodb') return 'Database not set to MongoDB, but MongoDB is in use, something has gone terribly wrong';
|
||||
if (typeof UserConfig.config.database.options != 'object') return 'MongoDB configuration missing';
|
||||
|
||||
let config = UserConfig.config.database.options;
|
||||
|
||||
// check the postgres config
|
||||
const checker = (val: string) => val != null && val !== '';
|
||||
const issue =
|
||||
!checker(config.host) ? 'Missing MongoDB Host'
|
||||
: !checker(config.user) ? 'Missing MongoDB User'
|
||||
: !checker(config.password) ? 'Missing MongoDB Password'
|
||||
: !checker(config.database) ? 'Missing MongoDB Database'
|
||||
// ! Blame VS Code for this weird indentation
|
||||
: undefined;
|
||||
|
||||
return issue;
|
||||
}
|
||||
|
||||
open(): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// validate config
|
||||
let configError = this._validateConfig();
|
||||
if (configError != null) throw new Error(configError);
|
||||
|
||||
let options = UserConfig.config.database!.options! as MongoDBConfiguration;
|
||||
|
||||
// connect
|
||||
log.info('MongoDB', `connecting to ${options.host}:${options.port}`);
|
||||
this._client = await mongoose.connect(`mongodb://${options.user}:${options.password}@${options.host}:${options.port}/${options.database}`);
|
||||
log.success('MongoDB', 'ok');
|
||||
|
||||
resolve();
|
||||
} catch (err) {
|
||||
log.error('MongoDB', 'failed to connect');
|
||||
console.error(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// gracefully disconnect
|
||||
await this._client.disconnect();
|
||||
|
||||
resolve();
|
||||
} catch (err) {
|
||||
log.error('MongoDB', 'failed to disconnect');
|
||||
console.error(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
configure(): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
this._versionModel = this._client.model('assversions', VERSIONS_SCHEMA);
|
||||
this._fileModel = this._client.model('assfiles', FILE_SCHEMA);
|
||||
this._tokenModel = this._client.model('asstokens', TOKEN_SCHEMA);
|
||||
this._userModel = this._client.model('assusers', USER_SCHEMA);
|
||||
|
||||
// theres only one version right now so we dont need to worry about anything, just adding the version thingies if they arent there
|
||||
let versions = await this._versionModel.find().exec()
|
||||
.then(res => res.reduce((obj, doc) => obj.set(doc.name, doc.version), new Map<string, number>()));
|
||||
|
||||
for (let [table, version] of [['assfiles', 1], ['asstokens', 1], ['assusers', 1]] as [string, number][]) {
|
||||
if (!versions.has(table)) {
|
||||
// set the version
|
||||
new this._versionModel({
|
||||
name: table,
|
||||
version: version
|
||||
}).save();
|
||||
|
||||
versions.set(table, version);
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
} catch (err) {
|
||||
log.error('MongoDB', 'failed to configure');
|
||||
console.error(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
put(table: DatabaseTable, key: string, data: DatabaseValue): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const models = {
|
||||
assfiles: this._fileModel,
|
||||
assusers: this._userModel,
|
||||
asstokens: this._tokenModel
|
||||
};
|
||||
|
||||
await new models[table]({
|
||||
id: key,
|
||||
data: data
|
||||
}).save();
|
||||
|
||||
resolve();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get(table: DatabaseTable, key: string): Promise<DatabaseValue | undefined> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const models = {
|
||||
assfiles: this._fileModel,
|
||||
assusers: this._userModel,
|
||||
asstokens: this._tokenModel
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
// typescript cant infer this but it is 100% correct
|
||||
// no need to worry :>
|
||||
let result = await models[table].find({
|
||||
id: key
|
||||
}).exec();
|
||||
|
||||
resolve(result.length ? result[0].data : void 0);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getAll(table: DatabaseTable): Promise<{ [index: string]: DatabaseValue; }> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const models = {
|
||||
assfiles: this._fileModel,
|
||||
assusers: this._userModel,
|
||||
asstokens: this._tokenModel
|
||||
};
|
||||
|
||||
// more ts-ignore!
|
||||
// @ts-ignore
|
||||
let result = await models[table].find({}).exec() // @ts-ignore
|
||||
.then(res => res.reduce((obj, doc) => (obj[doc.id] = doc.data, obj), {}));
|
||||
|
||||
resolve(result);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
Loading…
Reference in new issue