mirror of https://github.com/tycrek/ass
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
12 KiB
363 lines
12 KiB
// Default configuration
|
|
const config = {
|
|
host: '0.0.0.0',
|
|
port: 40115,
|
|
domain: 'upload.example.com',
|
|
maxUploadSize: 50,
|
|
useSsl: true,
|
|
isProxied: true,
|
|
resourceIdSize: 12,
|
|
gfyIdSize: 2,
|
|
resourceIdType: 'random',
|
|
spaceReplace: '_',
|
|
mediaStrict: false,
|
|
viewDirect: false,
|
|
useIdInViewer: false,
|
|
idInViewerExtension: false,
|
|
dataEngine: '@tycrek/papito',
|
|
frontendName: 'ass-x',
|
|
savePerDay: false,
|
|
adminWebhookEnabled: false,
|
|
s3enabled: false,
|
|
};
|
|
|
|
// Default admin webhook
|
|
const adminWebhookConfig = {
|
|
adminWebhookUrl: '',
|
|
adminWebhookUsername: '',
|
|
adminWebhookAvatar: '',
|
|
}
|
|
|
|
// Default S3 config
|
|
const s3config = {
|
|
s3endpoint: 'sfo3.digitaloceanspaces.com',
|
|
s3bucket: 'bucket-name',
|
|
s3usePathStyle: false,
|
|
s3accessKey: 'accessKey',
|
|
s3secretKey: 'secretKey',
|
|
};
|
|
|
|
// Redacted configs from previous versions
|
|
const oldConfig = {
|
|
// Note for people manually editing config.json
|
|
__WARNING__: "The following configs are no longer used and are here for backwards compatibility. For optimal use, DO NOT edit them.",
|
|
|
|
// Removed in 0.8.4
|
|
diskFilePath: 'uploads/',
|
|
saveWithDate: true, // Some systems don't like dirs with massive amounts of files
|
|
saveAsOriginal: false, // Prone to conflicts, which ass doesn't handle
|
|
useSia: false, // Sia has been shut down in 2022, uploads fail as of 2022-12-26
|
|
};
|
|
|
|
function getConfirmSchema(description) {
|
|
return {
|
|
properties: {
|
|
confirm: {
|
|
description,
|
|
type: 'string',
|
|
pattern: /^[y|n]/gim,
|
|
message: 'Must respond with either \'y\' or \'n\'',
|
|
required: true,
|
|
before: (value) => value.toLowerCase().startsWith('y')
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// If directly called on the command line, run setup script
|
|
function doSetup() {
|
|
const path = (...paths) => require('path').join(process.cwd(), ...paths);
|
|
const { TLog } = require('@tycrek/log');
|
|
const fs = require('fs-extra');
|
|
const prompt = require('prompt');
|
|
const chalk = require('chalk');
|
|
const token = require('./generators/token');
|
|
|
|
const log = new TLog('debug').setTimestamp({ enabled: false });
|
|
|
|
// Override default configs with existing configs to allow migrating configs
|
|
// Now that's a lot of configs!
|
|
try {
|
|
const existingConfig = require('../config.json');
|
|
Object.entries(existingConfig).forEach(([key, value]) => {
|
|
Object.prototype.hasOwnProperty.call(config, key) && (config[key] = value); // skipcq: JS-0093
|
|
Object.prototype.hasOwnProperty.call(adminWebhookConfig, key) && (adminWebhookConfig[key] = value); // skipcq: JS-0093
|
|
Object.prototype.hasOwnProperty.call(s3config, key) && (s3config[key] = value); // skipcq: JS-0093
|
|
Object.prototype.hasOwnProperty.call(oldConfig, key) && (oldConfig[key] = value); // skipcq: JS-0093
|
|
});
|
|
} catch (ex) {
|
|
if (ex.code !== 'MODULE_NOT_FOUND' && !ex.toString().includes('Unexpected end')) log.error(`${ex}`);
|
|
}
|
|
|
|
// Disabled the annoying "prompt: " prefix and removes colours
|
|
prompt.message = '';
|
|
prompt.colors = false;
|
|
prompt.start();
|
|
|
|
// Schema for setup prompts
|
|
const setupSchema = {
|
|
properties: {
|
|
host: {
|
|
description: 'Local IP to bind to',
|
|
type: 'string',
|
|
default: config.host,
|
|
required: false
|
|
},
|
|
port: {
|
|
description: 'Port number to listen on',
|
|
type: 'integer',
|
|
default: config.port,
|
|
required: false
|
|
},
|
|
domain: {
|
|
description: `Domain name to send to ShareX clients (example: ${config.domain})`,
|
|
type: 'string',
|
|
required: false,
|
|
message: 'You must input a valid domain name or IP to continue'
|
|
},
|
|
maxUploadSize: {
|
|
description: `Maximum size for uploaded files, in megabytes`,
|
|
type: 'integer',
|
|
default: config.maxUploadSize,
|
|
require: false
|
|
},
|
|
isProxied: {
|
|
description: 'Will you be running through a reverse proxy',
|
|
type: 'boolean',
|
|
default: config.isProxied,
|
|
required: false
|
|
},
|
|
useSsl: {
|
|
description: 'Use HTTPS (must be configured with reverse proxy)',
|
|
type: 'boolean',
|
|
default: config.useSsl,
|
|
required: false
|
|
},
|
|
resourceIdSize: {
|
|
description: 'URL length (length of ID\'s for your files, recommended: 6-15. Higher = more uploads, but longer URLs)',
|
|
type: 'integer',
|
|
default: config.resourceIdSize,
|
|
required: false
|
|
},
|
|
resourceIdType: {
|
|
description: 'URL type (can be one of: zws, random, gfycat, original, timestamp)',
|
|
type: 'string',
|
|
default: config.resourceIdType,
|
|
require: false,
|
|
pattern: /(original|zws|random|gfycat|timestamp)/gi, // skipcq: JS-0113
|
|
message: 'Must be one of: zws, random, gfycat, original, timestamp'
|
|
},
|
|
spaceReplace: {
|
|
description: 'Character to replace spaces in filenames with (must be a hyphen -, underscore _, or use ! to remove spaces)',
|
|
type: 'string',
|
|
default: config.spaceReplace,
|
|
required: false,
|
|
pattern: /^[-_!]$/gim,
|
|
message: 'Must be a - , _ , or !'
|
|
},
|
|
gfyIdSize: {
|
|
description: 'Adjective count for "gfycat" URL type',
|
|
type: 'integer',
|
|
default: config.gfyIdSize,
|
|
required: false
|
|
},
|
|
mediaStrict: {
|
|
description: 'Only allow uploads of media files (images, videos, audio)',
|
|
type: 'boolean',
|
|
default: config.mediaStrict,
|
|
required: false
|
|
},
|
|
viewDirect: {
|
|
description: 'View uploads in browser as direct resource, rather than a viewing page',
|
|
type: 'boolean',
|
|
default: config.viewDirect,
|
|
required: false
|
|
},
|
|
useIdInViewer: {
|
|
description: 'Use the ID in the web viewer instead of the filename',
|
|
type: 'boolean',
|
|
default: config.useIdInViewer,
|
|
required: false
|
|
},
|
|
idInViewerExtension: {
|
|
description: '(Only applies if "useIdInViewer" is true) Include the file extension in the ID in the web viewer',
|
|
type: 'boolean',
|
|
default: config.idInViewerExtension,
|
|
required: false
|
|
},
|
|
dataEngine: {
|
|
description: 'Data engine to use (must match an npm package name. If unsure, leave blank)',
|
|
type: 'string',
|
|
default: config.dataEngine,
|
|
required: false
|
|
},
|
|
frontendName: {
|
|
description: 'Name of your frontend (leave blank if not using frontends)',
|
|
type: 'string',
|
|
default: config.frontendName,
|
|
required: false
|
|
},
|
|
savePerDay: {
|
|
description: 'Save uploads in folders by day (YYYY-MM-DD) instead of by month (YYYY-MM)',
|
|
type: 'boolean',
|
|
default: config.savePerDay,
|
|
required: false
|
|
},
|
|
adminWebhookEnabled: {
|
|
description: 'Enable admin Discord webhook (This will let you audit ALL uploads, from ALL users)',
|
|
type: 'boolean',
|
|
default: config.adminWebhookEnabled,
|
|
required: false
|
|
},
|
|
s3enabled: {
|
|
description: 'Enable uploading to S3 storage endpoints',
|
|
type: 'boolean',
|
|
default: config.s3enabled,
|
|
required: false
|
|
}
|
|
}
|
|
};
|
|
|
|
const adminWebhookSchema = {
|
|
properties: {
|
|
adminWebhookUrl: {
|
|
description: 'Discord webhook URL for admin notifications',
|
|
type: 'string',
|
|
default: adminWebhookConfig.adminWebhookUrl,
|
|
required: true
|
|
},
|
|
adminWebhookUsername: {
|
|
description: 'Username to send the webhook as',
|
|
type: 'string',
|
|
default: adminWebhookConfig.adminWebhookUsername,
|
|
required: false
|
|
},
|
|
adminWebhookAvatar: {
|
|
description: 'Avatar to use for the webhook icon',
|
|
type: 'string',
|
|
default: adminWebhookConfig.adminWebhookAvatar,
|
|
required: false
|
|
}
|
|
}
|
|
};
|
|
|
|
const s3schema = {
|
|
properties: {
|
|
s3endpoint: {
|
|
description: 'S3 Endpoint URL to upload objects to',
|
|
type: 'string',
|
|
default: s3config.s3endpoint,
|
|
required: false
|
|
},
|
|
s3bucket: {
|
|
description: 'S3 Bucket name to upload objects to',
|
|
type: 'string',
|
|
default: s3config.s3bucket,
|
|
required: false
|
|
},
|
|
s3usePathStyle: {
|
|
description: 'S3 path endpoint, otherwise uses subdomain endpoint',
|
|
type: 'boolean',
|
|
default: s3config.s3usePathStyle,
|
|
required: false
|
|
},
|
|
s3accessKey: {
|
|
description: 'Access key for the specified S3 API',
|
|
type: 'string',
|
|
default: s3config.s3accessKey,
|
|
required: false
|
|
},
|
|
s3secretKey: {
|
|
description: 'Secret key for the specified S3 API',
|
|
type: 'string',
|
|
default: s3config.s3secretKey,
|
|
required: false
|
|
},
|
|
}
|
|
};
|
|
|
|
// Schema for confirm prompt. User must enter 'y' or 'n' (case-insensitive)
|
|
const confirmSchema = getConfirmSchema('\nIs the above information correct? (y/n)');
|
|
|
|
log.blank().blank().blank().blank()
|
|
.info('<<< ass setup >>>').blank();
|
|
let results = {};
|
|
prompt.get(setupSchema)
|
|
.then((r) => results = r) // skipcq: JS-0086
|
|
|
|
// Check if using admin webhook
|
|
.then(() => results.adminWebhookEnabled ? prompt.get(adminWebhookSchema) : adminWebhookConfig) // skipcq: JS-0229
|
|
.then((r) => Object.entries(r).forEach(([k, v]) => results[k] = v)) // skipcq: JS-0086
|
|
// Check if using S3
|
|
.then(() => results.s3enabled ? prompt.get(s3schema) : s3config) // skipcq: JS-0229
|
|
.then((r) => Object.entries(r).forEach(([k, v]) => results[k] = v)) // skipcq: JS-0086
|
|
|
|
// Verify information is correct
|
|
.then(() => log
|
|
.blank()
|
|
.info('Please verify your information', '\n'.concat(Object.entries(results).map(([setting, value]) => `${' '}${chalk.dim.gray('-->')} ${chalk.bold.white(`${setting}:`)} ${chalk.white(value)}`).join('\n')))
|
|
.blank())
|
|
|
|
// Apply old configs
|
|
.then(() => Object.entries(oldConfig).forEach(([setting, value]) => (typeof results[setting] === 'undefined') && (results[setting] = value)))
|
|
|
|
// Confirm
|
|
.then(() => prompt.get(confirmSchema))
|
|
.then(({ confirm }) => (confirm ? fs.writeJson(path('config.json'), results, { spaces: 4 }) : log.error('Setup aborted').callback(() => process.exit(1))))
|
|
|
|
// Other setup tasks
|
|
.then(() => {
|
|
|
|
// Make sure auth.json exists and generate the first key
|
|
if (!fs.existsSync(path('auth.json')) || fs.readFileSync(path('auth.json')).length < 8) {
|
|
let users = {};
|
|
users[token()] = { username: 'ass', count: 0 };
|
|
fs.writeJsonSync(path('auth.json'), { users }, { spaces: 4 });
|
|
log.debug('File created', 'auth.json')
|
|
.success('!! Important', `Save this token in a secure spot: ${Object.keys(users)[0]}`)
|
|
.blank();
|
|
}
|
|
|
|
let existingData = {}
|
|
try {
|
|
existingData = fs.readJsonSync(path('data.json'));
|
|
} catch (ex) {
|
|
log.warn('data.json', 'File empty, fixing')
|
|
}
|
|
|
|
// All 3 as a Promise.all
|
|
return Promise.all([
|
|
fs.ensureDir(path('share')),
|
|
fs.ensureDir(path(results.diskFilePath, 'thumbnails')),
|
|
fs.writeJson(path('data.json'), existingData, { spaces: 4 })
|
|
]);
|
|
})
|
|
|
|
// Complete & exit
|
|
.then(() => log.blank().success('Setup complete').callback(() => process.exit(0)))
|
|
.catch((err) => log.blank().callback(() => console.error(err)).callback(() => process.exit(1)));
|
|
}
|
|
|
|
module.exports = {
|
|
doSetup,
|
|
config
|
|
};
|
|
|
|
// If called on the command line, run the setup.
|
|
// Using this makes sure setup is not run when imported by another file
|
|
if (require.main === module) {
|
|
doSetup();
|
|
}
|
|
|
|
/*{
|
|
description: 'Enter your password', // Prompt displayed to the user. If not supplied name will be used.
|
|
type: 'string', // Specify the type of input to expect.
|
|
pattern: /^\w+$/, // Regular expression that input must be valid against.
|
|
message: 'Password must be letters', // Warning message to display if validation fails.
|
|
hidden: true, // If true, characters entered will either not be output to console or will be outputed using the `replace` string.
|
|
replace: '*', // If `hidden` is set it will replace each hidden character with the specified string.
|
|
default: 'lamepassword', // Default value to use if no value is entered.
|
|
required: true, // If true, value entered must be non-empty.
|
|
before: function (value) { return 'v' + value; } // Runs before node-prompt callbacks. It modifies user's input
|
|
}*/
|