Database migration and seeding
This commit is contained in:
parent
25c5a06ec3
commit
44e6fd31d2
|
@ -0,0 +1,21 @@
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
client: process.env.DB_CLIENT,
|
||||||
|
connection: {
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE
|
||||||
|
},
|
||||||
|
pool: {
|
||||||
|
min: process.env.DATABASE_POOL_MIN || 2,
|
||||||
|
max: process.env.DATABASE_POOL_MAX || 10
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
directory: 'src/api/database/migrations'
|
||||||
|
},
|
||||||
|
seeds: {
|
||||||
|
directory: 'src/api/database/seeds'
|
||||||
|
}
|
||||||
|
};
|
11
package.json
11
package.json
|
@ -10,11 +10,14 @@
|
||||||
},
|
},
|
||||||
"main": "src/_scripts/start.js",
|
"main": "src/_scripts/start.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "node src/wizard.js",
|
"setup": "yarn build && node src/wizard.js",
|
||||||
"api": "node src/api/structures/Server",
|
"dev": "nuxt",
|
||||||
"site": "nuxt",
|
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
"start": "cross-env NODE_ENV=production node src/start && nuxt start"
|
"migrate": "yarn knex migrate:latest",
|
||||||
|
"seed": "yarn knex seed:run",
|
||||||
|
"api": "node src/api/structures/Server",
|
||||||
|
"site": "cross-env NODE_ENV=production nuxt start",
|
||||||
|
"update": "git pull && yarn migrate && yarn build"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
exports.up = async knex => {
|
||||||
|
await knex.schema.createTable('users', table => {
|
||||||
|
table.increments();
|
||||||
|
table.string('username');
|
||||||
|
table.string('password');
|
||||||
|
table.boolean('enabled').defaultTo(true);
|
||||||
|
table.boolean('isAdmin').defaultTo(false);
|
||||||
|
table.string('apiKey');
|
||||||
|
table.timestamp('passwordEditedAt');
|
||||||
|
table.timestamp('apiKeyEditedAt');
|
||||||
|
table.timestamp('createdAt');
|
||||||
|
table.timestamp('editedAt');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.createTable('albums', table => {
|
||||||
|
table.increments();
|
||||||
|
table.integer('userId');
|
||||||
|
table.string('name');
|
||||||
|
table.timestamp('zippedAt');
|
||||||
|
table.timestamp('createdAt');
|
||||||
|
table.timestamp('editedAt');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.createTable('files', table => {
|
||||||
|
table.increments();
|
||||||
|
table.integer('userId');
|
||||||
|
table.string('name');
|
||||||
|
table.string('original');
|
||||||
|
table.string('type');
|
||||||
|
table.integer('size');
|
||||||
|
table.string('hash');
|
||||||
|
table.string('ip');
|
||||||
|
table.timestamp('createdAt');
|
||||||
|
table.timestamp('editedAt');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.createTable('links', table => {
|
||||||
|
table.increments();
|
||||||
|
table.integer('userId');
|
||||||
|
table.integer('albumId');
|
||||||
|
table.string('identifier');
|
||||||
|
table.integer('views').defaultTo(0);
|
||||||
|
table.boolean('enabled').defaultTo(true);
|
||||||
|
table.boolean('enableDownload').defaultTo(true);
|
||||||
|
table.timestamp('expiresAt');
|
||||||
|
table.timestamp('createdAt');
|
||||||
|
table.timestamp('editedAt');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.createTable('albumsFiles', table => {
|
||||||
|
table.increments();
|
||||||
|
table.integer('albumId');
|
||||||
|
table.integer('fileId');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.createTable('albumsLinks', table => {
|
||||||
|
table.increments();
|
||||||
|
table.integer('albumId');
|
||||||
|
table.integer('linkId');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.down = async knex => {
|
||||||
|
await knex.schema.dropTableIfExists('users');
|
||||||
|
await knex.schema.dropTableIfExists('albums');
|
||||||
|
await knex.schema.dropTableIfExists('files');
|
||||||
|
await knex.schema.dropTableIfExists('links');
|
||||||
|
await knex.schema.dropTableIfExists('albumsFiles');
|
||||||
|
await knex.schema.dropTableIfExists('albumsLinks');
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const moment = require('moment');
|
||||||
|
const randomstring = require('randomstring');
|
||||||
|
|
||||||
|
exports.seed = async db => {
|
||||||
|
const now = moment.utc().toDate();
|
||||||
|
const user = await db.table('users').where({ username: 'root' }).first();
|
||||||
|
if (user) return;
|
||||||
|
try {
|
||||||
|
const hash = await bcrypt.hash(process.env.ADMIN_PASSWORD, 10);
|
||||||
|
await db.table('users').insert({
|
||||||
|
username: process.env.ADMIN_ACCOUNT,
|
||||||
|
password: hash,
|
||||||
|
apiKey: randomstring.generate(64),
|
||||||
|
passwordEditedAt: now,
|
||||||
|
apiKeyEditedAt: now,
|
||||||
|
createdAt: now,
|
||||||
|
editedAt: now,
|
||||||
|
isAdmin: true
|
||||||
|
});
|
||||||
|
console.log();
|
||||||
|
console.log('====================================================');
|
||||||
|
console.log('== Successfully created the admin account. ==');
|
||||||
|
console.log('====================================================');
|
||||||
|
console.log('== Run `yarn api` and `yarn site` next ==');
|
||||||
|
console.log('== preferably with pm2 or tmux to keep them alive ==');
|
||||||
|
console.log('====================================================');
|
||||||
|
console.log();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,112 +0,0 @@
|
||||||
const log = require('../utils/Log');
|
|
||||||
const { server } = require('../../../config');
|
|
||||||
const db = require('knex')(server.database);
|
|
||||||
const bcrypt = require('bcrypt');
|
|
||||||
const moment = require('moment');
|
|
||||||
const randomstring = require('randomstring');
|
|
||||||
|
|
||||||
class Database {
|
|
||||||
constructor() {
|
|
||||||
this.createTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
async createTables() {
|
|
||||||
if (!await db.schema.hasTable('users')) {
|
|
||||||
await db.schema.createTable('users', table => {
|
|
||||||
table.increments();
|
|
||||||
table.string('username');
|
|
||||||
table.string('password');
|
|
||||||
table.boolean('enabled').defaultTo(true);
|
|
||||||
table.boolean('isAdmin').defaultTo(false);
|
|
||||||
table.string('apiKey');
|
|
||||||
table.timestamp('passwordEditedAt');
|
|
||||||
table.timestamp('apiKeyEditedAt');
|
|
||||||
table.timestamp('createdAt');
|
|
||||||
table.timestamp('editedAt');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.schema.hasTable('albums')) {
|
|
||||||
await db.schema.createTable('albums', table => {
|
|
||||||
table.increments();
|
|
||||||
table.integer('userId');
|
|
||||||
table.string('name');
|
|
||||||
// table.string('identifier');
|
|
||||||
// table.boolean('enabled');
|
|
||||||
// table.boolean('enableDownload').defaultTo(true);
|
|
||||||
table.timestamp('zippedAt');
|
|
||||||
table.timestamp('createdAt');
|
|
||||||
table.timestamp('editedAt');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.schema.hasTable('files')) {
|
|
||||||
await db.schema.createTable('files', table => {
|
|
||||||
table.increments();
|
|
||||||
table.integer('userId');
|
|
||||||
table.string('name');
|
|
||||||
table.string('original');
|
|
||||||
table.string('type');
|
|
||||||
table.integer('size');
|
|
||||||
table.string('hash');
|
|
||||||
table.string('ip');
|
|
||||||
table.timestamp('createdAt');
|
|
||||||
table.timestamp('editedAt');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.schema.hasTable('links')) {
|
|
||||||
await db.schema.createTable('links', table => {
|
|
||||||
table.increments();
|
|
||||||
table.integer('userId');
|
|
||||||
table.integer('albumId');
|
|
||||||
table.string('identifier');
|
|
||||||
table.integer('views').defaultTo(0);
|
|
||||||
table.boolean('enabled').defaultTo(true);
|
|
||||||
table.boolean('enableDownload').defaultTo(true);
|
|
||||||
table.timestamp('expiresAt');
|
|
||||||
table.timestamp('createdAt');
|
|
||||||
table.timestamp('editedAt');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.schema.hasTable('albumsFiles')) {
|
|
||||||
await db.schema.createTable('albumsFiles', table => {
|
|
||||||
table.increments();
|
|
||||||
table.integer('albumId');
|
|
||||||
table.integer('fileId');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.schema.hasTable('albumsLinks')) {
|
|
||||||
await db.schema.createTable('albumsLinks', table => {
|
|
||||||
table.increments();
|
|
||||||
table.integer('albumId');
|
|
||||||
table.integer('linkId');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = moment.utc().toDate();
|
|
||||||
const user = await db.table('users').where({ username: 'root' }).first();
|
|
||||||
if (user) return;
|
|
||||||
try {
|
|
||||||
const hash = await bcrypt.hash('root', 10);
|
|
||||||
await db.table('users').insert({
|
|
||||||
username: 'root',
|
|
||||||
password: hash,
|
|
||||||
apiKey: randomstring.generate(64),
|
|
||||||
passwordEditedAt: now,
|
|
||||||
apiKeyEditedAt: now,
|
|
||||||
createdAt: now,
|
|
||||||
editedAt: now,
|
|
||||||
isAdmin: true
|
|
||||||
});
|
|
||||||
log.success('Successfully created the root user with password "root". Make sure to log in and change it!');
|
|
||||||
} catch (error) {
|
|
||||||
log.error(error);
|
|
||||||
if (error) log.error('Error generating password hash for root');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Database;
|
|
|
@ -8,7 +8,6 @@ const RateLimit = require('express-rate-limit');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const jetpack = require('fs-jetpack');
|
const jetpack = require('fs-jetpack');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Database = require('./Database');
|
|
||||||
|
|
||||||
const rateLimiter = new RateLimit({
|
const rateLimiter = new RateLimit({
|
||||||
windowMs: process.env.RATE_LIMIT_WINDOW,
|
windowMs: process.env.RATE_LIMIT_WINDOW,
|
||||||
|
@ -35,7 +34,6 @@ class Server {
|
||||||
this.server.use(bodyParser.json());
|
this.server.use(bodyParser.json());
|
||||||
// this.server.use(rateLimiter);
|
// this.server.use(rateLimiter);
|
||||||
this.routesFolder = path.join(__dirname, '..', 'routes');
|
this.routesFolder = path.join(__dirname, '..', 'routes');
|
||||||
this.database = new Database();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAllTheRoutes() {
|
registerAllTheRoutes() {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
const randomstring = require('randomstring');
|
||||||
const jetpack = require('fs-jetpack');
|
const jetpack = require('fs-jetpack');
|
||||||
const qoa = require('qoa');
|
const qoa = require('qoa');
|
||||||
qoa.config({
|
qoa.config({
|
||||||
|
@ -15,6 +16,10 @@ async function start() {
|
||||||
});
|
});
|
||||||
if (!confirm.run) process.exit(0);
|
if (!confirm.run) process.exit(0);
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
console.log('You can manually edit .env file after the wizard to edit values');
|
||||||
|
console.log();
|
||||||
|
|
||||||
const wizard = [
|
const wizard = [
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
@ -93,10 +98,35 @@ async function start() {
|
||||||
accept: 'y',
|
accept: 'y',
|
||||||
deny: 'n'
|
deny: 'n'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
query: 'Name of the admin account?',
|
||||||
|
handle: 'ADMIN_ACCOUNT'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'secure',
|
type: 'secure',
|
||||||
query: 'Type a secure password for the root user:',
|
query: 'Type a secure password for the root user:',
|
||||||
handle: 'ROOT_PASSWORD'
|
handle: 'ADMIN_PASSWORD'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
query: 'Database host',
|
||||||
|
handle: 'DB_HOST'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
query: 'Database user',
|
||||||
|
handle: 'DB_USER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
query: 'Database password',
|
||||||
|
handle: 'DB_PASSWORD'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
query: 'Database name',
|
||||||
|
handle: 'DB_DATABASE'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -109,13 +139,9 @@ async function start() {
|
||||||
RATE_LIMIT_WINDOW: 2,
|
RATE_LIMIT_WINDOW: 2,
|
||||||
RATE_LIMIT_MAX: 5,
|
RATE_LIMIT_MAX: 5,
|
||||||
DB_CLIENT: 'pg',
|
DB_CLIENT: 'pg',
|
||||||
DB_HOST: 'localhost',
|
|
||||||
DB_USER: '',
|
|
||||||
DB_PASSWORD: '',
|
|
||||||
DB_DATABASE: '',
|
|
||||||
BLOCKED_EXTENSIONS: ['.jar', '.exe', '.msi', '.com', '.bat', '.cmd', '.scr', '.ps1', '.sh'],
|
BLOCKED_EXTENSIONS: ['.jar', '.exe', '.msi', '.com', '.bat', '.cmd', '.scr', '.ps1', '.sh'],
|
||||||
UPLOAD_FOLDER: 'uploads',
|
UPLOAD_FOLDER: 'uploads',
|
||||||
SECRET: 'SuperSecretPassphraseHere',
|
SECRET: randomstring.generate(64),
|
||||||
MAX_LINKS_PER_ALBUM: 5
|
MAX_LINKS_PER_ALBUM: 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -128,7 +154,11 @@ async function start() {
|
||||||
jetpack.write('.env', envfile);
|
jetpack.write('.env', envfile);
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
console.log('== .env file generated successfully. You can now run lolisafe ==');
|
console.log('=============================================');
|
||||||
|
console.log('== .env file generated successfully. ==');
|
||||||
|
console.log('=============================================');
|
||||||
|
console.log('== Run `yarn migrate` and `yarn seed` next ==');
|
||||||
|
console.log('=============================================');
|
||||||
console.log();
|
console.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue