diff --git a/package-lock.json b/package-lock.json index f77395a..e74cf7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "chibisafe", - "version": "4.0.1", + "version": "4.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1230,6 +1230,19 @@ } } }, + "@hapi/hoek": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz", + "integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw==" + }, + "@hapi/topo": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", + "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2763,6 +2776,24 @@ } } }, + "@sideway/address": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.0.tgz", + "integrity": "sha512-wAH/JYRXeIFQRsxerIuLjgUu2Xszam+O5xKeatJ4oudShOOirfmsQ1D6LL54XOU2tizpCYku+s1wmU0SYdpoSA==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "@sinonjs/commons": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", @@ -10859,6 +10890,18 @@ } } }, + "joi": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.3.0.tgz", + "integrity": "sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==", + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.0", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "js-base64": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", diff --git a/package.json b/package.json index 78460aa..6220941 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "fs-jetpack": "^2.2.2", "helmet": "^3.15.1", "imagesloaded": "^4.1.4", + "joi": "^17.3.0", "jsonwebtoken": "^8.5.0", "knex": "^0.21.15", "masonry-layout": "^4.2.2", diff --git a/src/api/structures/Setting.js b/src/api/structures/Setting.js new file mode 100644 index 0000000..068ebf1 --- /dev/null +++ b/src/api/structures/Setting.js @@ -0,0 +1,126 @@ +require('dotenv').config(); + +const Joi = require('joi'); +const { env } = process; + +const StatsGenerator = require('../utils/StatsGenerator'); + +// use label to name them nicely +// use meta to set custom rendering (render as radio instead of dropdown for example) and custom order +// use description to add comments which will show up as a note somewhere next to the option +const schema = Joi.object({ + // Server related settings + rateLimitWindow: Joi.number().integer().default(2) + .label('API rate limit window') + .description('Timeframe for which requests are checked/remembered'), + + rateLimitMax: Joi.number().integer().default(5) + .label('API maximum limit') + .description('Max number of connections during windowMs milliseconds before sending a 429 response'), + + // Service settings + serviceName: Joi.string().default('change-me') + .label('Service name') + .description('Name of the service'), + + domain: Joi.string().default(`http://localhost:${env.SERVER_PORT}`) + .label('Domain') + .description('Full domain this instance is gonna be running on'), + + // File related settings + chunkSize: Joi.number().integer().greater(0) + .default(90) + .label('Chunk size') + .description('Maximum size of a chunk (files bigger than this limit will be split into multiple chunks)'), + + maxSize: Joi.number().integer().min(0) // setting it to 0 disabled the limit + .default(5000) + .label('Maximum file size') + .description('Maximum allowed upload file size in MB (0 to disable)'), + + generateZips: Joi.boolean().default(true) + .label('Generate zips') + .description('Allows users to download entire albums in ZIP format'), + + generatedFileNameLength: Joi.number().integer().min(6) + .default(12) + .label('Generated file name length') + .description('How long should the automatically generated file name be'), + + generatedAlbumLength: Joi.number().integer().min(6) + .default(6) + .label('Generated album name length') + .description('How long should the automatically generated album identifier be'), + + maxLinksPerAlbum: Joi.number().integer().greater(0) + .default(5) + .label('Maximum album links') + .description('Maximum allowed number of a distinct links for an album'), + + uploadsFolder: Joi.string().default('uploads') + .label('Uploads folder') + .description('Name of the folder where the uploads will be stored'), + + blockedExtensions: Joi.array() + .items(Joi.string().pattern(/^(\.\w+)+$/, { name: 'file extension' })) + .default(['.jar', '.exe', '.msi', '.com', '.bat', '.cmd', '.scr', '.ps1', '.sh']) + .label('Blocked extensions') + .description('List of extensions which will be rejected by the server'), + + // User settings + publicMode: Joi.boolean().default(true) + .label('Public mode') + .description('Allows people to upload files without an account'), + + userAccount: Joi.boolean().default(true) + .label('User creation') + .description('Allows people to create new accounts'), + + // Social and sharing + metaThemeColor: Joi.string().hex().min(3) + .max(6) + .default('20222b') + .label('Meta theme color') + .description('Color that user agents should use to customize the display of the page/embeds'), + + metaDescription: Joi.string().default('Blazing fast file uploader and bunker written in node! 🚀') + .label('Meta description') + .description('Short and accurate summary of the content of the page'), + + metaKeyword: Joi.string().default('chibisafe,lolisafe,upload,uploader,file,vue,images,ssr,file uploader,free') + .label('Meta keywords') + .description('Words relevant to the page\'s content separated by commas'), + + metaTwitterHandle: Joi.string().pattern(/^@\w{1,15}$/, { name: 'twitter handle' }) + .label('Twitter handle') + .description('Your twitter handle'), + + // Instance settings + backgroundImageURL: Joi.string().uri().default(p => `${p.domain}/assets/images/background.jpg`) + .label('Background image link') + .description('Background image that should be used instead of the default background'), + + logoURL: Joi.string().uri().default(p => `${p.domain}/assets/images/logo.jpg`) + .label('Logo image link') + .description('Logo image that should be used instead of the default logo'), + + // Statistics settings + // TODO: Pattern fails for patterns like 0 1,2-7 * * * * because of the mixing of lists and ranges + statisticsCron: Joi.string().pattern(/((((\d+,)+\d+|([\d\*]+(\/|-)\d+)|\d+|\*) ?){6})/, { name: 'cron' }).default('0 0 * * * *') + .label('Statistics schedule') + .description('Crontab like formated value which will be used to schedule generating and saving stats to the database'), + + enabledStatistics: Joi.array().items(Joi.string().valid(...Object.keys(StatsGenerator.statGenerators)).optional()) + .meta({ displayType: 'checkbox' }) + .label('Enabled statistics') + .description('Which statistics should be shown when opening the statistics page'), + + savedStatistics: Joi.array().items(Joi.string().valid(...Object.keys(StatsGenerator.statGenerators)).optional()) + .meta({ displayType: 'checkbox' }) + .label('Cached statistics') + .description('Which statistics should be saved to the database (refer to Statistics schedule for scheduling). If a statistics is enabled but not set to be saved, it will be generated every time the statistics page is opened') +}); + +// schema._ids._byKey.keys() + +module.exports.schema = schema;