diff --git a/nuxt.config.js b/nuxt.config.js index a7dc5e49..30860a2b 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -1,5 +1,3 @@ -const conf = require('config') - module.exports = { telemetry: false, modern: (process.env.NODE_ENV === 'production') && 'client', @@ -15,8 +13,6 @@ module.exports = { }, dev: (process.env.NODE_ENV !== 'production'), - server: conf.server, - /* ** Customize the progress-bar color */ diff --git a/server/api/controller/event.js b/server/api/controller/event.js index 828aa526..c7c567ab 100644 --- a/server/api/controller/event.js +++ b/server/api/controller/event.js @@ -1,6 +1,6 @@ const crypto = require('crypto') const path = require('path') -const config = require('config') +const config = require('../../config') const fs = require('fs') const { Op } = require('sequelize') const intersection = require('lodash/intersection') diff --git a/server/api/controller/settings.js b/server/api/controller/settings.js index 2307b138..758816d4 100644 --- a/server/api/controller/settings.js +++ b/server/api/controller/settings.js @@ -1,6 +1,4 @@ -const Setting = require('../models/setting') -const config = require('config') -const consola = require('consola') +const config = require('../../config') const path = require('path') const fs = require('fs') const pkg = require('../../../package.json') @@ -11,9 +9,11 @@ const sharp = require('sharp') const log = require('../../log') const defaultSettings = { + title: 'Gancio', + description: 'A shared agenda for local communities', instance_timezone: 'Europe/Rome', instance_locale: 'en', - instance_name: config.title.toLowerCase().replace(/ /g, ''), + instance_name: 'gancio', // config.title.toLowerCase().replace(/ /g, ''), instance_place: '', allow_registration: true, allow_anon_event: true, @@ -42,6 +42,11 @@ const settingsController = { secretSettings: {}, async load () { + if (config.firstrun) { + settingsController.settings = defaultSettings + return + } + const Setting = require('../models/setting') if (!settingsController.settings.initialized) { // initialize instance settings from db // note that this is done only once when the server starts @@ -80,7 +85,7 @@ const settingsController = { if (config.user_locale && fs.existsSync(path.resolve(config.user_locale))) { const user_locale = fs.readdirSync(path.resolve(config.user_locale)) user_locale.forEach(async f => { - consola.info(`Loading user locale ${f}`) + log.info(`Loading user locale ${f}`) const locale = path.basename(f, '.js') settingsController.user_locale[locale] = (await require(path.resolve(config.user_locale, f))).default @@ -90,6 +95,7 @@ const settingsController = { }, async set (key, value, is_secret = false) { + const Setting = require('../models/setting') log.info(`SET ${key} ${is_secret ? '*****' : value}`) try { const [setting, created] = await Setting.findOrCreate({ @@ -135,14 +141,7 @@ const settingsController = { getAllRequest (req, res) { // get public settings and public configuration - const settings = { - ...settingsController.settings, - baseurl: config.baseurl, - title: config.title, - description: config.description, - version: pkg.version - } - res.json(settings) + res.json({ ...settingsController.settings, version: pkg.version }) } } diff --git a/server/api/controller/user.js b/server/api/controller/user.js index dd5d3107..995f0bcf 100644 --- a/server/api/controller/user.js +++ b/server/api/controller/user.js @@ -1,6 +1,6 @@ const crypto = require('crypto') const { Op } = require('sequelize') -const config = require('config') +const config = require('../../config') const mail = require('../mail') const User = require('../models/user') const settingsController = require('./settings') diff --git a/server/api/mail.js b/server/api/mail.js index 105e68ca..6d281477 100644 --- a/server/api/mail.js +++ b/server/api/mail.js @@ -1,14 +1,14 @@ const Email = require('email-templates') const path = require('path') const moment = require('dayjs') -const config = require('config') -const settingsController = require('./controller/settings') +const config = require('../config') +const settings = require('./controller/settings').settings const log = require('../log') const { Task, TaskManager } = require('../taskManager') const locales = require('../../locales') const mail = { - send (addresses, template, locals, locale = settingsController.settings.instance_locale) { + send (addresses, template, locals, locale = settings.instance_locale) { log.debug('Enqueue new email ', template, locale) const task = new Task({ name: 'MAIL', @@ -31,7 +31,7 @@ const mail = { } }, message: { - from: `📅 ${config.title} <${config.admin_email}>` + from: `📅 ${settings.title} <${settings.admin_email}>` }, send: true, i18n: { @@ -39,23 +39,23 @@ const mail = { objectNotation: true, syncFiles: false, updateFiles: false, - defaultLocale: settingsController.settings.instance_locale || 'en', + defaultLocale: settings.instance_locale || 'en', locale, locales: Object.keys(locales) }, - transport: config.smtp + transport: settings.smtp }) const msg = { template, message: { to: addresses, - bcc: config.admin_email + bcc: settings.admin_email }, locals: { ...locals, locale, - config: { title: config.title, baseurl: config.baseurl, description: config.description, admin_email: config.admin_email }, + config: { title: settings.title, baseurl: settings.baseurl, description: settings.description, admin_email: settings.admin_email }, datetime: datetime => moment.unix(datetime).locale(locale).format('ddd, D MMMM HH:mm') } } diff --git a/server/api/storage.js b/server/api/storage.js index e44f7d8f..adeb2733 100644 --- a/server/api/storage.js +++ b/server/api/storage.js @@ -4,7 +4,7 @@ const crypto = require('crypto') const mkdirp = require('mkdirp') const sharp = require('sharp') const log = require('../log') -const config = require('config') +const config = require('../config') try { mkdirp.sync(config.upload_path + '/thumb') diff --git a/server/cli.js b/server/cli.js index 2dee2057..bdbf0a4d 100755 --- a/server/cli.js +++ b/server/cli.js @@ -1,328 +1,58 @@ #!/usr/bin/env node process.env.NODE_ENV = 'production' -const fs = require('fs') -const consola = require('consola') -const Sequelize = require('sequelize') -const inquirer = require('inquirer') const pkg = require('../package.json') -const firstrun = require('./firstrun') const path = require('path') -const mkdirp = require('mkdirp') -const url = require('url') const cwd = process.cwd() const data_path = process.env.GANCIO_DATA || path.resolve('./') // needed by nuxt -process.chdir(path.resolve(__dirname, '..')) +// process.chdir(path.resolve(__dirname, '..')) -function notEmpty (value) { - return value.length > 0 -} - -async function setupQuestionnaire (is_docker, db) { - const questions = [] - questions.push({ - name: 'title', - message: 'Name of your instance', - default: 'Gancio', - validate: notEmpty - }) - - questions.push({ - message: - 'Specify a baseurl for this gancio installation! (eg. http://gancio.cisti.org)', - name: 'baseurl', - default: 'http://localhost:13120', - validate: value => { - if (!value) { - return false - } - return /^https?:\/\//.test(value) - } - }) - - 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', - default: 13120 - }) - - questions.push({ - name: 'db.dialect', - message: 'DB dialect', - type: 'list', - choices: ['sqlite', 'postgres'] - }) - - questions.push({ - name: 'db.storage', - message: 'sqlite db path', - default: path.resolve(data_path, 'db.sqlite'), - filter: p => path.resolve(cwd, p), - when: answers => answers.db.dialect === 'sqlite', - validate: db_path => - db_path.length > 0 && fs.existsSync(path.dirname(db_path)) - }) - - 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 - }) - - questions.push({ - name: 'db.username', - message: 'DB user', - default: 'gancio', - when: answers => answers.db.dialect === 'postgres', - validate: notEmpty - }) - - questions.push({ - name: 'db.password', - type: 'password', - message: 'DB password', - default: 'gancio', - when: answers => answers.db.dialect === 'postgres', - validate: (password, options) => { - try { - const db = new Sequelize({ - ...options.db, - dialect: 'postgres', - password, - logging: false - }) - return db.authenticate().then(() => { - db.close() - return true - }) - } catch (e) { - consola.error(e) - return false - } - } - }) - - questions.push({ - name: 'upload_path', - message: 'Where gancio has to store media?', - default: './uploads', - filter: p => path.resolve(cwd, p), - validate: p => { - const exists = fs.existsSync(p) - if (!exists) { - consola.warn(`"${p}" does not exists, trying to create it`) - try { - mkdirp.sync(p) - consola.info(`${p} succesfully created`) - } catch (e) { - console.error(String(e)) - return false - } - } - return true - } - }) - - questions.push({ - name: 'log_path', - message: 'Log path', - default: path.resolve(data_path, 'logs') - }) - } - - questions.push({ - name: 'admin.email', - message: 'Admin email', - default: options => { - const baseurl = new url.URL(options.baseurl) - return ( - options.title.replace(' ', '').toLowerCase() + '@' + baseurl.hostname - ) - }, - validate: notEmpty - }) - - questions.push({ - name: 'admin.password', - message: 'Admin password', - type: 'password', - validate: notEmpty - }) - - questions.push({ - name: 'smtp_type', - message: 'How should we send the emails ?', - type: 'list', - choices: ['SMTP', 'sendmail', 'None (choose later)'] - }) - - 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)) - }) - - questions.push({ - name: 'smtp.host', - message: 'SMTP Host', - default: 'localhost', - validate: notEmpty, - when: answers => answers.smtp_type === 'SMTP' - }) - - questions.push({ - name: 'smtp.secure', - message: 'Does SMTP server support TLS?', - when: answers => answers.smtp_type === 'SMTP' && !['localhost', '127.0.0.1'].includes(answers.smtp.host), - 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), - when: answers => answers.smtp_type === 'SMTP' - }) - - questions.push({ - name: 'smtp_need_auth', - message: 'is SMTP authentication needed?', - type: 'confirm', - default: answers => !['localhost', '127.0.0.1'].includes(answers.smtp.host), - when: answers => answers.smtp_type === 'SMTP' - }) - - questions.push({ - name: 'smtp.auth.user', - message: 'SMTP User', - validate: notEmpty, - default: answers => answers.admin.email, - when: answers => answers.smtp_type === 'SMTP' && answers.smtp_need_auth - }) - - questions.push({ - name: 'smtp.auth.pass', - message: 'SMTP Password', - type: 'password', - validate: notEmpty, - when: answers => answers.smtp_type === 'SMTP' && answers.smtp_need_auth - }) - - const answers = await inquirer.prompt(questions) - if (is_docker) { - answers.server = { host: '0.0.0.0', port: 13120 } - answers.upload_path = path.resolve(data_path, 'uploads') - answers.log_level = 'debug' - answers.log_path = path.resolve(data_path, 'logs') - if (db === 'sqlite') { - answers.db = { dialect: db, storage: path.resolve(data_path, 'db.sqlite') } - } else { - answers.db = { - dialect: db, - host: 'db', - database: 'gancio', - username: 'gancio', - password: 'gancio' - } - } - } - - return answers -} - -async function run_migrations (db_conf) { - const Umzug = require('umzug') - const Sequelize = require('sequelize') - 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) - } -} +// async function run_migrations (db_conf) { +// const Umzug = require('umzug') +// const Sequelize = require('sequelize') +// 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) +// } +// } async function start (options) { - // is first run? - if (firstrun.check(options.config)) { - if (options.docker) { - consola.error( - '⚠ ️ Something goes wrong, did you run "docker-compose run --rm gancio gancio setup --docker --db="' - ) - process.exit(-1) - } - consola.error(` ⚠ Configuration file "${options.config}" not found! Use "--config " to specify another path. -If this is your first run use 'gancio setup --config ' `) + try { + const config = require('./config') + config.load() + console.info(`Logging to ${path.resolve(`${config.log_path}/gancio.log`)} [level: ${config.log_level}]`) + } catch (e) { + console.error(e) process.exit(-1) } - const config = require('config') - await run_migrations(config.db) - consola.info(`Logging to ${path.resolve(`${config.log_path}/gancio.log`)} [level: ${config.log_level}]`) require('./index') } -async function setup (options) { - consola.info('You\'re going to setup gancio on this machine.') - const config = await setupQuestionnaire(options.docker, options.db) - await run_migrations(config.db) - const ret = await firstrun.setup(config, options.config) - if (!ret) { - process.exit(-1) - } - if (options.docker) { - consola.info('You can edit ./data/config.json to modify your configuration.') - consola.info('Start the server with "docker-compose up"') - } else { - consola.info( - `You can edit '${options.config}' to modify your configuration. ` - ) - consola.info(`Start the server with "gancio --config ${options.config}"`) - } - process.exit(0) -} - -consola.info(`📅 ${pkg.name} - v${pkg.version} - ${pkg.description} (nodejs: ${process.version})`) +// async function setup (options) +console.info(`📅 ${pkg.name} - v${pkg.version} - ${pkg.description} (nodejs: ${process.version})`) require('yargs') .usage('Usage $0 [options]') @@ -346,7 +76,6 @@ require('yargs') 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') diff --git a/server/config.js b/server/config.js new file mode 100644 index 00000000..1a84d887 --- /dev/null +++ b/server/config.js @@ -0,0 +1,52 @@ +const fs = require('fs') +const path = require('path') +const log = require('./log') + +let config = { + firstrun: true, + // title: "Gancio", + // description: "A shared agenda for local communities", + baseurl: "http://localhost:13120", + server: { + host: '127.0.0.1', + port: 13120 + }, + log_level: 'debug', + log_path: './logs', + db: {}, + upload_path: './uploads', + // smtp: { + // auth: { + // user: '', + // pass: '' + // }, + // secure: true, + // host: '' + // }, + // admin_email: '', + + // + write (config_path= process.env.config_path || './config.json') { + log.error(path.resolve(config_path)) + return fs.writeFileSync(config_path, JSON.stringify(config, null, 2)) + }, + + load () { + // load configuration from file + console.error(process.env.NODE_ENV) + const config_path = process.env.config_path || './config.json' + log.info(`Reading configuration from: ${config_path}`) + if (fs.existsSync(config_path)) { + const configContent = fs.readFileSync(config_path) + config = Object.assign(config, JSON.parse(configContent)) + config.firstrun = false + } else { + config.firstrun = true + log.error('configuration file does not exists! we cannot be here!') + } + } +} + +config.load() + +module.exports = config \ No newline at end of file diff --git a/server/dbconfig.js b/server/dbconfig.js index 69ec6c05..77b63199 100644 --- a/server/dbconfig.js +++ b/server/dbconfig.js @@ -1,3 +1,3 @@ // needed by sequelize -const config = require('config') +const config = require('./config') module.exports = config.db diff --git a/server/federation/ego.js b/server/federation/ego.js index 2e87252c..7c31182f 100644 --- a/server/federation/ego.js +++ b/server/federation/ego.js @@ -1,5 +1,5 @@ const Event = require('../api/models/event') -const config = require('config') +const config = require('../config') const log = require('../log') module.exports = { diff --git a/server/federation/follows.js b/server/federation/follows.js index c60aa761..45f7321e 100644 --- a/server/federation/follows.js +++ b/server/federation/follows.js @@ -1,4 +1,4 @@ -const config = require('config') +const config = require('../config') const Helpers = require('./helpers') const crypto = require('crypto') const log = require('../log') diff --git a/server/federation/helpers.js b/server/federation/helpers.js index 8267465f..431f525a 100644 --- a/server/federation/helpers.js +++ b/server/federation/helpers.js @@ -1,7 +1,7 @@ const axios = require('axios') // const request = require('request') const crypto = require('crypto') -const config = require('config') +const config = require('../config') const httpSignature = require('http-signature') const APUser = require('../api/models/ap_user') const Instance = require('../api/models/instance') diff --git a/server/federation/nodeinfo.js b/server/federation/nodeinfo.js index 21363147..08321968 100644 --- a/server/federation/nodeinfo.js +++ b/server/federation/nodeinfo.js @@ -2,7 +2,7 @@ const express = require('express') const router = express.Router() const cors = require('cors') const settingsController = require('../api/controller/settings') -const config = require('config') +const config = require('../config') const version = require('../../package.json').version router.use(cors()) diff --git a/server/federation/users.js b/server/federation/users.js index 9079af50..d5d26341 100644 --- a/server/federation/users.js +++ b/server/federation/users.js @@ -3,7 +3,7 @@ const Place = require('../api/models/place') const APUser = require('../api/models/ap_user') const Tag = require('../api/models/tag') -const config = require('config') +const config = require('../config') const log = require('../log') const utc = require('dayjs/plugin/utc') const dayjs = require('dayjs') diff --git a/server/firstrun.js b/server/firstrun.js index d793b502..a0bd6533 100644 --- a/server/firstrun.js +++ b/server/firstrun.js @@ -1,61 +1,62 @@ -// check config.js existance -const fs = require('fs') -const consola = require('consola') +// // check config.js existance +// const fs = require('fs') -module.exports = { - check (config_path) { - return !fs.existsSync(config_path) - }, +// module.exports = { +// check (config_path) { +// const ret = !fs.existsSync(config_path) +// log.warn(`check firstrun: ${ret} - ${config_path}`) +// return ret +// }, - async setup (config, config_path) { - // generate a random salt - // consola.info('Generate random salt') - // config.secret = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) +// async setup (config, config_path) { +// // generate a random salt +// // consola.info('Generate random salt') +// // config.secret = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) - // do not save admin's password in config file - const admin = { email: config.admin.email, password: config.admin.password } - delete config.admin +// // do not save admin's password in config file +// const admin = { email: config.admin.email, password: config.admin.password } +// delete config.admin - if (config.smtp_type === 'sendmail') { - config.smtp = { - sendmail: true, - newline: 'unix', - path: config.smtp.path - } - } - delete config.smtp_type - delete config.smtp_need_auth - config.admin_email = admin.email - config.db.logging = false - config.log_level = 'debug' - consola.info(`Save configuration to ${config_path}`) - try { - fs.writeFileSync(config_path, JSON.stringify(config, null, 2)) - } catch (e) { - consola.warn(` ⚠️ ${e}. You can specify configuration path using '--config'`) - } +// if (config.smtp_type === 'sendmail') { +// config.smtp = { +// sendmail: true, +// newline: 'unix', +// path: config.smtp.path +// } +// } +// delete config.smtp_type +// delete config.smtp_need_auth +// config.admin_email = admin.email +// config.db.logging = false +// config.log_level = 'debug' +// console.info(`Save configuration to ${config_path}`) +// try { +// fs.writeFileSync(config_path, JSON.stringify(config, null, 2)) +// } catch (e) { +// console.warn(` ⚠️ ${e}. You can specify configuration path using '--config'`) +// } - // sync db - const db = require('./api/models/index') - const User = require('./api/models/user') - const users = await User.findAll() - if (users.length) { - consola.warn(' ⚠ Non empty db! Please move your current db elsewhere than retry.') - return false - } +// // sync db +// const db = require('./api/models/index') +// const User = require('./api/models/user') +// const users = await User.findAll() +// if (users.length) { +// console.warn(' ⚠ Non empty db! Please move your current db elsewhere than retry.') +// return false +// } - // create admin user - consola.info(`Create admin with email: ${admin.email}`) - await User.create({ - email: admin.email, - password: admin.password, - is_admin: true, - is_active: true - }) +// // create admin user +// console.info(`Create admin with email: ${admin.email}`) +// await User.create({ +// email: admin.email, +// password: admin.password, +// is_admin: true, +// is_active: true +// }) - // close db connection - await db.close() +// // close db connection +// await db.close() - return true - } -} +// return true +// } +// } diff --git a/server/helpers.js b/server/helpers.js index ddafa517..1e518cd3 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -6,7 +6,7 @@ const dayjs = require('dayjs') const timezone = require('dayjs/plugin/timezone') dayjs.extend(timezone) -const config = require('config') +const config = require('./config') const log = require('./log') const pkg = require('../package.json') const fs = require('fs') @@ -61,7 +61,8 @@ module.exports = { req.settings = settingsController.settings req.secretSettings = settingsController.secretSettings - req.settings.baseurl = config.baseurl + req.settings.baseurl = config.baseurl || req.protocol + '://' + req.headers.host + req.settings.hostname = new URL.URL(req.settings.baseurl).hostname req.settings.title = req.settings.title || config.title req.settings.description = req.settings.description || config.description req.settings.version = pkg.version @@ -187,4 +188,6 @@ module.exports = { log.debug(cursor) return cursor } + + } diff --git a/server/index.js b/server/index.js index 0a8330a5..7d0979f8 100644 --- a/server/index.js +++ b/server/index.js @@ -2,7 +2,7 @@ const { Nuxt, Builder } = require('nuxt') // Import and Set Nuxt.js options const nuxtConfig = require('../nuxt.config.js') -const config = require('config') +const config = require('./config') const log = require('./log') async function main () { @@ -29,14 +29,17 @@ async function main () { return } - const { TaskManager } = require('./taskManager') - TaskManager.start() + let TaskManager + if (!config.firstrun) { + TaskManager = require('./taskManager').TaskManager + TaskManager.start() + } const msg = `Listen on ${config.server.host}:${config.server.port} , visit me here => ${config.baseurl}` log.info(msg) // close connections/port/unix socket function shutdown () { - TaskManager.stop() + if (TaskManager) { TaskManager.stop() } nuxt.close(async () => { log.info('Closing DB') const sequelize = require('./api/models') diff --git a/server/log.js b/server/log.js index b4aaf0be..d29ea588 100644 --- a/server/log.js +++ b/server/log.js @@ -1,7 +1,6 @@ const { createLogger, transports, format } = require('winston') const DailyRotateFile = require('winston-daily-rotate-file') -// const dayjs = require('dayjs') -const config = require('config') +const config = require('./config') const gancioFormat = format.printf(info => { if (info.stack) { diff --git a/server/notifier.js b/server/notifier.js index 5a8b55b4..8e1ceea7 100644 --- a/server/notifier.js +++ b/server/notifier.js @@ -1,5 +1,5 @@ const mail = require('./api/mail') -const config = require('config') +const config = require('./config') const log = require('./log') const fediverseHelpers = require('./federation/helpers')