2019-06-21 23:52:18 +02:00
|
|
|
|
#!/usr/bin/env node
|
2019-07-03 16:58:24 +02:00
|
|
|
|
process.env.NODE_ENV = 'production'
|
2019-06-25 01:05:38 +02:00
|
|
|
|
|
2019-07-03 16:58:24 +02:00
|
|
|
|
const fs = require('fs')
|
|
|
|
|
const consola = require('consola')
|
2019-11-06 11:31:56 +01:00
|
|
|
|
const Sequelize = require('sequelize')
|
2019-06-21 23:52:18 +02:00
|
|
|
|
const inquirer = require('inquirer')
|
2019-11-06 11:31:56 +01:00
|
|
|
|
const pkg = require('../package.json')
|
2019-06-21 23:52:18 +02:00
|
|
|
|
const firstrun = require('./firstrun')
|
2019-07-03 16:58:24 +02:00
|
|
|
|
const path = require('path')
|
2019-08-05 01:23:02 +02:00
|
|
|
|
const mkdirp = require('mkdirp')
|
2019-09-25 19:07:15 +02:00
|
|
|
|
const url = require('url')
|
2019-06-21 23:52:18 +02:00
|
|
|
|
|
2019-07-03 16:58:24 +02:00
|
|
|
|
const cwd = process.cwd()
|
2021-06-04 15:36:03 +02:00
|
|
|
|
const data_path = process.env.GANCIO_DATA || path.resolve('./')
|
2019-07-03 16:58:24 +02:00
|
|
|
|
|
|
|
|
|
// needed by nuxt
|
|
|
|
|
process.chdir(path.resolve(__dirname, '..'))
|
2019-06-21 23:52:18 +02:00
|
|
|
|
|
2019-06-26 14:44:21 +02:00
|
|
|
|
function notEmpty (value) {
|
2019-11-06 11:31:56 +01:00
|
|
|
|
return value.length > 0
|
2019-06-26 14:44:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-06 11:31:56 +01:00
|
|
|
|
async function setupQuestionnaire (is_docker, db) {
|
2019-06-21 23:52:18 +02:00
|
|
|
|
const questions = []
|
2019-07-03 16:58:24 +02:00
|
|
|
|
questions.push({
|
|
|
|
|
name: 'title',
|
|
|
|
|
message: 'Name of your instance',
|
|
|
|
|
default: 'Gancio',
|
|
|
|
|
validate: notEmpty
|
|
|
|
|
})
|
|
|
|
|
|
2019-06-21 23:52:18 +02:00
|
|
|
|
questions.push({
|
2019-11-06 11:31:56 +01:00
|
|
|
|
message:
|
|
|
|
|
'Specify a baseurl for this gancio installation! (eg. http://gancio.cisti.org)',
|
2019-06-21 23:52:18 +02:00
|
|
|
|
name: 'baseurl',
|
2019-07-03 16:58:24 +02:00
|
|
|
|
default: 'http://localhost:13120',
|
2019-09-24 11:46:11 +02:00
|
|
|
|
validate: value => {
|
2019-11-06 11:31:56 +01:00
|
|
|
|
if (!value) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2019-09-24 11:46:11 +02:00
|
|
|
|
return /^https?:\/\//.test(value)
|
|
|
|
|
}
|
2019-06-26 14:44:21 +02:00
|
|
|
|
})
|
|
|
|
|
|
2019-09-25 19:07:15 +02:00
|
|
|
|
if (!is_docker) {
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'server.host',
|
|
|
|
|
message: 'address to listen to',
|
|
|
|
|
default: 'localhost',
|
|
|
|
|
validate: notEmpty
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'server.port',
|
|
|
|
|
message: 'port to listen to',
|
2019-11-06 11:31:56 +01:00
|
|
|
|
default: 13120
|
2019-09-25 19:07:15 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'db.dialect',
|
|
|
|
|
message: 'DB dialect',
|
|
|
|
|
type: 'list',
|
|
|
|
|
choices: ['sqlite', 'postgres']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'db.storage',
|
|
|
|
|
message: 'sqlite db path',
|
2021-06-04 15:36:03 +02:00
|
|
|
|
default: path.resolve(data_path, 'db.sqlite'),
|
2019-09-25 19:07:15 +02:00
|
|
|
|
filter: p => path.resolve(cwd, p),
|
|
|
|
|
when: answers => answers.db.dialect === 'sqlite',
|
2019-11-06 11:31:56 +01:00
|
|
|
|
validate: db_path =>
|
|
|
|
|
db_path.length > 0 && fs.existsSync(path.dirname(db_path))
|
2019-09-25 19:07:15 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'db.host',
|
|
|
|
|
message: 'Postgres host',
|
|
|
|
|
default: 'localhost',
|
|
|
|
|
when: answers => answers.db.dialect === 'postgres',
|
|
|
|
|
validate: notEmpty
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'db.database',
|
|
|
|
|
message: 'DB name',
|
|
|
|
|
default: 'gancio',
|
|
|
|
|
when: answers => answers.db.dialect === 'postgres',
|
|
|
|
|
validate: notEmpty
|
|
|
|
|
})
|
2019-11-06 11:31:56 +01:00
|
|
|
|
|
2019-09-25 19:07:15 +02:00
|
|
|
|
questions.push({
|
|
|
|
|
name: 'db.username',
|
|
|
|
|
message: 'DB user',
|
|
|
|
|
default: 'gancio',
|
|
|
|
|
when: answers => answers.db.dialect === 'postgres',
|
|
|
|
|
validate: notEmpty
|
|
|
|
|
})
|
2019-11-06 11:31:56 +01:00
|
|
|
|
|
2019-09-25 19:07:15 +02:00
|
|
|
|
questions.push({
|
|
|
|
|
name: 'db.password',
|
|
|
|
|
type: 'password',
|
|
|
|
|
message: 'DB password',
|
|
|
|
|
default: 'gancio',
|
|
|
|
|
when: answers => answers.db.dialect === 'postgres',
|
2019-11-06 11:31:56 +01:00
|
|
|
|
validate: (password, options) => {
|
2019-08-05 01:23:02 +02:00
|
|
|
|
try {
|
2019-11-06 11:31:56 +01:00
|
|
|
|
const db = new Sequelize({
|
|
|
|
|
...options.db,
|
|
|
|
|
dialect: 'postgres',
|
|
|
|
|
password,
|
|
|
|
|
logging: false
|
|
|
|
|
})
|
|
|
|
|
return db.authenticate().then(() => {
|
2019-09-25 19:07:15 +02:00
|
|
|
|
db.close()
|
|
|
|
|
return true
|
|
|
|
|
})
|
2019-11-06 11:31:56 +01:00
|
|
|
|
} catch (e) {
|
2019-09-25 19:07:15 +02:00
|
|
|
|
consola.error(e)
|
2019-08-05 01:23:02 +02:00
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-25 19:07:15 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'upload_path',
|
|
|
|
|
message: 'Where gancio has to store media?',
|
|
|
|
|
default: './uploads',
|
|
|
|
|
filter: p => path.resolve(cwd, p),
|
2019-11-06 11:31:56 +01:00
|
|
|
|
validate: p => {
|
|
|
|
|
const exists = fs.existsSync(p)
|
2019-09-25 19:07:15 +02:00
|
|
|
|
if (!exists) {
|
|
|
|
|
consola.warn(`"${p}" does not exists, trying to create it`)
|
|
|
|
|
try {
|
|
|
|
|
mkdirp.sync(p)
|
2020-01-29 21:39:02 +01:00
|
|
|
|
consola.info(`${p} succesfully created`)
|
2019-11-06 11:31:56 +01:00
|
|
|
|
} catch (e) {
|
2019-09-25 19:07:15 +02:00
|
|
|
|
console.error(String(e))
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
})
|
2021-06-04 15:36:03 +02:00
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'log_path',
|
|
|
|
|
message: 'Log path',
|
|
|
|
|
default: path.resolve(data_path, 'logs')
|
|
|
|
|
})
|
2019-09-25 19:07:15 +02:00
|
|
|
|
}
|
2021-06-04 15:36:03 +02:00
|
|
|
|
|
2019-06-21 23:52:18 +02:00
|
|
|
|
questions.push({
|
|
|
|
|
name: 'admin.email',
|
2020-02-15 15:42:41 +01:00
|
|
|
|
message: 'Admin email',
|
2019-09-24 11:46:11 +02:00
|
|
|
|
default: options => {
|
2019-11-13 10:47:24 +01:00
|
|
|
|
const baseurl = new url.URL(options.baseurl)
|
2019-11-06 11:31:56 +01:00
|
|
|
|
return (
|
|
|
|
|
options.title.replace(' ', '').toLowerCase() + '@' + baseurl.hostname
|
|
|
|
|
)
|
2019-09-24 11:46:11 +02:00
|
|
|
|
},
|
2019-06-26 14:44:21 +02:00
|
|
|
|
validate: notEmpty
|
2019-06-21 23:52:18 +02:00
|
|
|
|
})
|
2019-11-06 11:31:56 +01:00
|
|
|
|
|
2019-06-21 23:52:18 +02:00
|
|
|
|
questions.push({
|
|
|
|
|
name: 'admin.password',
|
|
|
|
|
message: 'Admin password',
|
|
|
|
|
type: 'password',
|
2019-06-26 14:44:21 +02:00
|
|
|
|
validate: notEmpty
|
|
|
|
|
})
|
|
|
|
|
|
2020-01-29 21:39:02 +01:00
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp_type',
|
|
|
|
|
message: 'How should we send the emails ?',
|
|
|
|
|
type: 'list',
|
2021-07-01 10:59:53 +02:00
|
|
|
|
choices: ['SMTP', 'sendmail', 'None (choose later)']
|
2020-01-29 21:39:02 +01:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp.path',
|
|
|
|
|
message: 'Where sendmail binary is ?',
|
|
|
|
|
default: '/usr/sbin/sendmail',
|
|
|
|
|
when: answers => answers.smtp_type === 'sendmail',
|
|
|
|
|
validate: sendmail_path => sendmail_path.length > 0 && fs.existsSync(path.resolve(sendmail_path))
|
|
|
|
|
})
|
|
|
|
|
|
2019-06-26 14:44:21 +02:00
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp.host',
|
|
|
|
|
message: 'SMTP Host',
|
2020-01-29 21:39:02 +01:00
|
|
|
|
default: 'localhost',
|
|
|
|
|
validate: notEmpty,
|
2021-07-01 10:59:53 +02:00
|
|
|
|
when: answers => answers.smtp_type === 'SMTP'
|
2020-01-29 21:39:02 +01:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp.secure',
|
|
|
|
|
message: 'Does SMTP server support TLS?',
|
2021-07-01 10:59:53 +02:00
|
|
|
|
when: answers => answers.smtp_type === 'SMTP' && !['localhost', '127.0.0.1'].includes(answers.smtp.host),
|
2020-01-29 21:39:02 +01:00
|
|
|
|
default: true,
|
|
|
|
|
type: 'confirm'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp.port',
|
|
|
|
|
message: 'SMTP Port',
|
|
|
|
|
default: answers => ['localhost', '127.0.0.1'].includes(answers.smtp.host) ? 25 : (answers.smtp.secure ? 465 : 587),
|
2021-07-01 10:59:53 +02:00
|
|
|
|
when: answers => answers.smtp_type === 'SMTP'
|
2020-01-29 21:39:02 +01:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp_need_auth',
|
|
|
|
|
message: 'is SMTP authentication needed?',
|
|
|
|
|
type: 'confirm',
|
|
|
|
|
default: answers => !['localhost', '127.0.0.1'].includes(answers.smtp.host),
|
2021-07-01 10:59:53 +02:00
|
|
|
|
when: answers => answers.smtp_type === 'SMTP'
|
2019-06-26 14:44:21 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp.auth.user',
|
|
|
|
|
message: 'SMTP User',
|
|
|
|
|
validate: notEmpty,
|
2020-01-29 21:39:02 +01:00
|
|
|
|
default: answers => answers.admin.email,
|
2021-07-01 10:59:53 +02:00
|
|
|
|
when: answers => answers.smtp_type === 'SMTP' && answers.smtp_need_auth
|
2019-06-26 14:44:21 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
questions.push({
|
|
|
|
|
name: 'smtp.auth.pass',
|
|
|
|
|
message: 'SMTP Password',
|
|
|
|
|
type: 'password',
|
2020-01-29 21:39:02 +01:00
|
|
|
|
validate: notEmpty,
|
2021-07-01 10:59:53 +02:00
|
|
|
|
when: answers => answers.smtp_type === 'SMTP' && answers.smtp_need_auth
|
2019-06-21 23:52:18 +02:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const answers = await inquirer.prompt(questions)
|
2019-09-25 19:07:15 +02:00
|
|
|
|
if (is_docker) {
|
|
|
|
|
answers.server = { host: '0.0.0.0', port: 13120 }
|
2021-06-04 15:36:03 +02:00
|
|
|
|
answers.upload_path = path.resolve(data_path, 'uploads')
|
2021-05-27 00:04:10 +02:00
|
|
|
|
answers.log_level = 'debug'
|
2021-06-04 15:36:03 +02:00
|
|
|
|
answers.log_path = path.resolve(data_path, 'logs')
|
2019-11-06 11:31:56 +01:00
|
|
|
|
if (db === 'sqlite') {
|
2021-06-04 15:36:03 +02:00
|
|
|
|
answers.db = { dialect: db, storage: path.resolve(data_path, 'db.sqlite') }
|
2019-09-25 19:07:15 +02:00
|
|
|
|
} else {
|
2019-11-06 11:31:56 +01:00
|
|
|
|
answers.db = {
|
|
|
|
|
dialect: db,
|
|
|
|
|
host: 'db',
|
|
|
|
|
database: 'gancio',
|
|
|
|
|
username: 'gancio',
|
|
|
|
|
password: 'gancio'
|
|
|
|
|
}
|
2019-09-25 19:07:15 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 23:52:18 +02:00
|
|
|
|
return answers
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 18:43:49 +02:00
|
|
|
|
async function run_migrations (db_conf) {
|
2019-08-04 23:32:45 +02:00
|
|
|
|
const Umzug = require('umzug')
|
|
|
|
|
const Sequelize = require('sequelize')
|
2021-06-04 15:36:03 +02:00
|
|
|
|
try {
|
|
|
|
|
const db = new Sequelize(db_conf)
|
|
|
|
|
const umzug = new Umzug({
|
|
|
|
|
storage: 'sequelize',
|
|
|
|
|
storageOptions: { sequelize: db },
|
|
|
|
|
logging: consola.info,
|
|
|
|
|
migrations: {
|
|
|
|
|
wrap: fun => {
|
|
|
|
|
return () =>
|
|
|
|
|
fun(db.queryInterface, Sequelize).catch(e => {
|
|
|
|
|
consola.error(e)
|
|
|
|
|
return false
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
path: path.resolve(__dirname, 'migrations')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
await umzug.up()
|
|
|
|
|
return db.close()
|
|
|
|
|
} catch (e) {
|
|
|
|
|
consola.warn(` ⚠️ Cannot connect to db, check your configuration => ${e}`)
|
|
|
|
|
process.exit(-1)
|
|
|
|
|
}
|
2019-08-04 23:32:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 16:58:24 +02:00
|
|
|
|
async function start (options) {
|
2019-06-21 23:52:18 +02:00
|
|
|
|
// is first run?
|
|
|
|
|
if (firstrun.check(options.config)) {
|
2019-08-05 01:23:02 +02:00
|
|
|
|
if (options.docker) {
|
2019-11-06 11:31:56 +01:00
|
|
|
|
consola.error(
|
2021-07-01 10:59:53 +02:00
|
|
|
|
'⚠ ️ Something goes wrong, did you run "docker-compose run --rm gancio gancio setup --docker --db=<YOUR_DB_DIALECT>"'
|
2019-11-06 11:31:56 +01:00
|
|
|
|
)
|
2019-08-05 01:23:02 +02:00
|
|
|
|
process.exit(-1)
|
|
|
|
|
}
|
2019-09-25 19:07:15 +02:00
|
|
|
|
consola.error(` ⚠ Configuration file "${options.config}" not found! Use "--config <CONFIG_FILE.json>" to specify another path.
|
2019-08-03 15:32:39 +02:00
|
|
|
|
If this is your first run use 'gancio setup --config <CONFIG_FILE.json>' `)
|
2019-06-21 23:52:18 +02:00
|
|
|
|
process.exit(-1)
|
|
|
|
|
}
|
2019-10-25 18:43:49 +02:00
|
|
|
|
const config = require('config')
|
|
|
|
|
await run_migrations(config.db)
|
2021-05-27 00:04:10 +02:00
|
|
|
|
consola.info(`Logging to ${path.resolve(`${config.log_path}/gancio.log`)} [level: ${config.log_level}]`)
|
2021-04-28 12:44:26 +02:00
|
|
|
|
|
2019-07-03 16:58:24 +02:00
|
|
|
|
require('./index')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function setup (options) {
|
2020-01-29 21:39:02 +01:00
|
|
|
|
consola.info('You\'re going to setup gancio on this machine.')
|
2019-08-06 01:12:05 +02:00
|
|
|
|
const config = await setupQuestionnaire(options.docker, options.db)
|
2019-10-25 18:43:49 +02:00
|
|
|
|
await run_migrations(config.db)
|
2019-08-06 01:12:05 +02:00
|
|
|
|
const ret = await firstrun.setup(config, options.config)
|
2019-11-06 11:31:56 +01:00
|
|
|
|
if (!ret) {
|
|
|
|
|
process.exit(-1)
|
|
|
|
|
}
|
2019-08-05 01:41:35 +02:00
|
|
|
|
if (options.docker) {
|
2021-06-04 15:36:03 +02:00
|
|
|
|
consola.info('You can edit ./data/config.json to modify your configuration.')
|
2020-01-29 21:39:02 +01:00
|
|
|
|
consola.info('Start the server with "docker-compose up"')
|
2019-08-05 01:41:35 +02:00
|
|
|
|
} else {
|
2019-11-06 11:31:56 +01:00
|
|
|
|
consola.info(
|
|
|
|
|
`You can edit '${options.config}' to modify your configuration. `
|
|
|
|
|
)
|
2019-08-05 01:41:35 +02:00
|
|
|
|
consola.info(`Start the server with "gancio --config ${options.config}"`)
|
|
|
|
|
}
|
2019-07-29 22:40:27 +02:00
|
|
|
|
process.exit(0)
|
2019-07-03 16:58:24 +02:00
|
|
|
|
}
|
2019-06-21 23:52:18 +02:00
|
|
|
|
|
2019-11-06 11:31:56 +01:00
|
|
|
|
consola.info(`📅 ${pkg.name} - v${pkg.version} - ${pkg.description}`)
|
2019-07-03 16:58:24 +02:00
|
|
|
|
|
|
|
|
|
require('yargs')
|
2019-11-06 11:31:56 +01:00
|
|
|
|
.usage('Usage $0 <command> [options]')
|
|
|
|
|
.option('docker', {
|
|
|
|
|
alias: 'd',
|
|
|
|
|
describe: 'Inside docker',
|
|
|
|
|
default: false,
|
|
|
|
|
type: 'boolean'
|
|
|
|
|
})
|
|
|
|
|
.option('db', {
|
|
|
|
|
describe: 'Specify db type'
|
|
|
|
|
})
|
|
|
|
|
.option('config', {
|
|
|
|
|
alias: 'c',
|
|
|
|
|
describe: 'Configuration file',
|
2021-06-04 15:36:03 +02:00
|
|
|
|
default: path.resolve(data_path, 'config.json')
|
2019-11-06 11:31:56 +01:00
|
|
|
|
})
|
|
|
|
|
.coerce('config', config_path => {
|
|
|
|
|
const absolute_config_path = path.resolve(cwd, config_path)
|
|
|
|
|
process.env.config_path = absolute_config_path
|
|
|
|
|
return absolute_config_path
|
|
|
|
|
})
|
|
|
|
|
.command(['start', 'run', '$0'], 'Start gancio', {}, start)
|
|
|
|
|
.command('setup', 'Setup a new instance', {}, setup)
|
|
|
|
|
.help('h')
|
|
|
|
|
.alias('h', 'help')
|
|
|
|
|
.epilog('Made with ❤ by underscore hacklab - https://gancio.org')
|
2019-11-06 14:47:44 +01:00
|
|
|
|
.argv
|