enigma-bbs/core/database.js

269 lines
6.5 KiB
JavaScript
Raw Normal View History

2014-10-20 05:30:44 +00:00
/* jslint node: true */
'use strict';
// ENiGMA½
const conf = require('./config.js');
2014-10-20 05:30:44 +00:00
// deps
const sqlite3 = require('sqlite3');
const paths = require('path');
const async = require('async');
2016-06-26 04:37:28 +00:00
const _ = require('lodash');
const assert = require('assert');
2014-10-21 04:47:13 +00:00
// database handles
let dbs = {};
2014-10-21 04:47:13 +00:00
2016-06-26 04:37:28 +00:00
exports.getModDatabasePath = getModDatabasePath;
2014-10-20 05:30:44 +00:00
exports.initializeDatabases = initializeDatabases;
2014-10-21 04:47:13 +00:00
exports.dbs = dbs;
2014-10-20 05:30:44 +00:00
function getDatabasePath(name) {
2016-06-26 04:37:28 +00:00
return paths.join(conf.config.paths.db, `${name}.sqlite3`);
}
function getModDatabasePath(moduleInfo, suffix) {
//
// Mods that use a database are stored in Config.paths.modsDb (e.g. enigma-bbs/db/mods)
// We expect that moduleInfo defines packageName which will be the base of the modules
// filename. An optional suffix may be supplied as well.
//
const HOST_RE = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
assert(_.isObject(moduleInfo));
assert(_.isString(moduleInfo.packageName), 'moduleInfo must define "packageName"!');
let full = moduleInfo.packageName;
if(suffix) {
full += `.${suffix}`;
}
assert(
(full.split('.').length > 1 && HOST_RE.test(full)),
'packageName must follow Reverse Domain Name Notation - https://en.wikipedia.org/wiki/Reverse_domain_name_notation');
return paths.join(conf.config.paths.modsDb, `${full}.sqlite3`);
2014-10-20 05:30:44 +00:00
}
2015-11-07 01:25:07 +00:00
function initializeDatabases(cb) {
async.series(
[
function systemDb(callback) {
dbs.system = new sqlite3.Database(getDatabasePath('system'), err => {
2015-11-07 01:25:07 +00:00
if(err) {
return callback(err);
2015-11-07 01:25:07 +00:00
}
dbs.system.serialize( () => {
createSystemTables();
});
return callback(null);
2015-11-07 01:25:07 +00:00
});
},
function userDb(callback) {
dbs.user = new sqlite3.Database(getDatabasePath('user'), err => {
if(err) {
return callback(err);
2015-11-07 01:25:07 +00:00
}
dbs.user.serialize( () => {
createUserTables();
createInitialUserValues();
});
return callback(null);
2015-11-07 01:25:07 +00:00
});
},
function messageDb(callback) {
dbs.message = new sqlite3.Database(getDatabasePath('message'), err => {
2015-11-07 01:25:07 +00:00
if(err) {
return callback(err);
2015-11-07 01:25:07 +00:00
}
dbs.message.serialize(function serialized() {
createMessageBaseTables();
createInitialMessageValues();
});
return callback(null);
2015-11-07 01:25:07 +00:00
});
}
],
cb
2015-11-07 01:25:07 +00:00
);
2014-10-20 05:30:44 +00:00
}
function createSystemTables() {
2016-07-07 02:55:50 +00:00
dbs.system.run('PRAGMA foreign_keys = ON;');
dbs.system.run(
'CREATE TABLE IF NOT EXISTS system_property (' +
' prop_name VARCHAR PRIMARY KEY NOT NULL,' +
' prop_value VARCHAR NOT NULL' +
');'
);
//
// system_log can round log_timestamp for daily, monthly, etc.
// statistics as well as unique entries.
//
/*
dbs.system.run(
'CREATE TABLE IF NOT EXISTS system_log (' +
' log_timestamp DATETIME PRIMARY KEY NOT NULL ( ' +
' log_name VARCHARNOT NULL,' +
' log_value VARCHAR NOT NULL,' +
' UNIQUE(log_timestamp, log_name)' +
');'
);*/
}
2014-10-20 05:30:44 +00:00
function createUserTables() {
2016-07-07 02:55:50 +00:00
dbs.user.run('PRAGMA foreign_keys = ON;');
2014-10-21 04:47:13 +00:00
dbs.user.run(
`CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY,
user_name VARCHAR NOT NULL,
UNIQUE(user_name)
);`
);
2014-10-20 05:30:44 +00:00
2014-10-21 04:47:13 +00:00
// :TODO: create FK on delete/etc.
dbs.user.run(
`CREATE TABLE IF NOT EXISTS user_property (
user_id INTEGER NOT NULL,
prop_name VARCHAR NOT NULL,
prop_value VARCHAR,
UNIQUE(user_id, prop_name),
FOREIGN KEY(user_id) REFERENCES user(id) ON DELETE CASCADE
);`
);
2015-05-11 03:39:39 +00:00
dbs.user.run(
`CREATE TABLE IF NOT EXISTS user_group_member (
group_name VARCHAR NOT NULL,
user_id INTEGER NOT NULL,
UNIQUE(group_name, user_id)
);`
);
dbs.user.run(
`CREATE TABLE IF NOT EXISTS user_login_history (
user_id INTEGER NOT NULL,
user_name VARCHAR NOT NULL,
timestamp DATETIME NOT NULL
);`
);
2015-05-11 03:39:39 +00:00
}
2015-06-26 05:19:32 +00:00
function createMessageBaseTables() {
2016-07-07 02:55:50 +00:00
dbs.message.run('PRAGMA foreign_keys = ON;');
dbs.message.run(
`CREATE TABLE IF NOT EXISTS message (
message_id INTEGER PRIMARY KEY,
area_tag VARCHAR NOT NULL,
message_uuid VARCHAR(36) NOT NULL,
reply_to_message_id INTEGER,
to_user_name VARCHAR NOT NULL,
from_user_name VARCHAR NOT NULL,
subject, /* FTS @ message_fts */
message, /* FTS @ message_fts */
modified_timestamp DATETIME NOT NULL,
view_count INTEGER NOT NULL DEFAULT 0,
UNIQUE(message_uuid)
);`
2015-06-26 05:19:32 +00:00
);
dbs.message.run(
`CREATE INDEX IF NOT EXISTS message_by_area_tag_index
ON message (area_tag);`
2015-06-26 05:19:32 +00:00
);
dbs.message.run(
`CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts4 (
content="message",
subject,
message
);`
);
dbs.message.run(
`CREATE TRIGGER IF NOT EXISTS message_before_update BEFORE UPDATE ON message BEGIN
DELETE FROM message_fts WHERE docid=old.rowid;
END;
CREATE TRIGGER IF NOT EXISTS message_before_delete BEFORE DELETE ON message BEGIN
DELETE FROM message_fts WHERE docid=old.rowid;
END;
CREATE TRIGGER IF NOT EXISTS message_after_update AFTER UPDATE ON message BEGIN
INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);
END;
CREATE TRIGGER IF NOT EXISTS message_after_insert AFTER INSERT ON message BEGIN
INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);
END;`
);
dbs.message.run(
`CREATE TABLE IF NOT EXISTS message_meta (
message_id INTEGER NOT NULL,
meta_category INTEGER NOT NULL,
meta_name VARCHAR NOT NULL,
meta_value VARCHAR NOT NULL,
UNIQUE(message_id, meta_category, meta_name, meta_value),
FOREIGN KEY(message_id) REFERENCES message(message_id) ON DELETE CASCADE
);`
);
// :TODO: need SQL to ensure cleaned up if delete from message?
/*
dbs.message.run(
`CREATE TABLE IF NOT EXISTS hash_tag (
hash_tag_id INTEGER PRIMARY KEY,
hash_tag_name VARCHAR NOT NULL,
UNIQUE(hash_tag_name)
);`
);
// :TODO: need SQL to ensure cleaned up if delete from message?
dbs.message.run(
`CREATE TABLE IF NOT EXISTS message_hash_tag (
hash_tag_id INTEGER NOT NULL,
message_id INTEGER NOT NULL,
);`
);
*/
dbs.message.run(
`CREATE TABLE IF NOT EXISTS user_message_area_last_read (
user_id INTEGER NOT NULL,
area_tag VARCHAR NOT NULL,
message_id INTEGER NOT NULL,
UNIQUE(user_id, area_tag)
);`
);
dbs.message.run(
`CREATE TABLE IF NOT EXISTS message_area_last_scan (
scan_toss VARCHAR NOT NULL,
area_tag VARCHAR NOT NULL,
message_id INTEGER NOT NULL,
UNIQUE(scan_toss, area_tag)
);`
);
2015-06-26 05:19:32 +00:00
}
function createInitialMessageValues() {
}
function createInitialUserValues() {
2014-10-20 05:30:44 +00:00
}