feat: built-in ssl support
- CORE_HTTPS is now CORE_RETURN_HTTPS - SSL_(KEY/CERT/ALLOW_HTTP1)
This commit is contained in:
parent
eadfa09570
commit
c21d8f837e
9 changed files with 61 additions and 21 deletions
|
@ -1,5 +1,5 @@
|
||||||
export interface ConfigCore {
|
export interface ConfigCore {
|
||||||
https: boolean;
|
return_https: boolean;
|
||||||
secret: string;
|
secret: string;
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
|
@ -134,6 +134,12 @@ export interface ConfigExif {
|
||||||
remove_gps: boolean;
|
remove_gps: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConfigSsl {
|
||||||
|
allow_http1: boolean;
|
||||||
|
key: string;
|
||||||
|
cert: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
core: ConfigCore;
|
core: ConfigCore;
|
||||||
uploader: ConfigUploader;
|
uploader: ConfigUploader;
|
||||||
|
@ -147,4 +153,5 @@ export interface Config {
|
||||||
chunks: ConfigChunks;
|
chunks: ConfigChunks;
|
||||||
mfa: ConfigMfa;
|
mfa: ConfigMfa;
|
||||||
exif: ConfigExif;
|
exif: ConfigExif;
|
||||||
|
ssl: ConfigSsl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { parse } from 'dotenv';
|
import { parse } from 'dotenv';
|
||||||
import { expand } from 'dotenv-expand';
|
import { expand } from 'dotenv-expand';
|
||||||
import { existsSync, readFileSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
import { resolve } from 'path';
|
||||||
import Logger from '../logger';
|
import Logger from '../logger';
|
||||||
import { humanToBytes } from '../utils/bytes';
|
import { humanToBytes } from '../utils/bytes';
|
||||||
|
|
||||||
export type ValueType = 'string' | 'number' | 'boolean' | 'array' | 'json-array' | 'human-to-byte';
|
export type ValueType = 'string' | 'number' | 'boolean' | 'array' | 'json-array' | 'human-to-byte' | 'path';
|
||||||
|
|
||||||
function isObject(value: any): value is Record<string, any> {
|
function isObject(value: any): value is Record<string, any> {
|
||||||
return typeof value === 'object' && value !== null;
|
return typeof value === 'object' && value !== null;
|
||||||
|
@ -55,7 +56,7 @@ export default function readConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const maps = [
|
const maps = [
|
||||||
map('CORE_HTTPS', 'boolean', 'core.https'),
|
map('CORE_RETURN_HTTPS', 'boolean', 'core.return_https'),
|
||||||
map('CORE_SECRET', 'string', 'core.secret'),
|
map('CORE_SECRET', 'string', 'core.secret'),
|
||||||
map('CORE_HOST', 'string', 'core.host'),
|
map('CORE_HOST', 'string', 'core.host'),
|
||||||
map('CORE_PORT', 'number', 'core.port'),
|
map('CORE_PORT', 'number', 'core.port'),
|
||||||
|
@ -150,6 +151,10 @@ export default function readConfig() {
|
||||||
|
|
||||||
map('EXIF_ENABLED', 'boolean', 'exif.enabled'),
|
map('EXIF_ENABLED', 'boolean', 'exif.enabled'),
|
||||||
map('EXIF_REMOVE_GPS', 'boolean', 'exif.remove_gps'),
|
map('EXIF_REMOVE_GPS', 'boolean', 'exif.remove_gps'),
|
||||||
|
|
||||||
|
map('SSL_KEY', 'path', 'ssl.key'),
|
||||||
|
map('SSL_CERT', 'path', 'ssl.cert'),
|
||||||
|
map('SSL_ALLOW_HTTP1', 'boolean', 'ssl.allow_http1'),
|
||||||
];
|
];
|
||||||
|
|
||||||
const config = {};
|
const config = {};
|
||||||
|
@ -186,6 +191,10 @@ export default function readConfig() {
|
||||||
parsed = humanToBytes(value) ?? undefined;
|
parsed = humanToBytes(value) ?? undefined;
|
||||||
if (!parsed) logger.debug(`Unable to parse ${map.env}=${value}`);
|
if (!parsed) logger.debug(`Unable to parse ${map.env}=${value}`);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'path':
|
||||||
|
parsed = resolve(value);
|
||||||
|
if (!existsSync(parsed)) logger.debug(`Unable to find ${map.env}=${value} (path does not exist)`);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
parsed = value;
|
parsed = value;
|
||||||
|
|
|
@ -23,7 +23,7 @@ const discord_content = s
|
||||||
|
|
||||||
const validator = s.object({
|
const validator = s.object({
|
||||||
core: s.object({
|
core: s.object({
|
||||||
https: s.boolean.default(false),
|
return_https: s.boolean.default(false),
|
||||||
secret: s.string.lengthGreaterThanOrEqual(8),
|
secret: s.string.lengthGreaterThanOrEqual(8),
|
||||||
host: s.string.default('0.0.0.0'),
|
host: s.string.default('0.0.0.0'),
|
||||||
port: s.number.default(3000),
|
port: s.number.default(3000),
|
||||||
|
@ -206,6 +206,13 @@ const validator = s.object({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
remove_gps: false,
|
remove_gps: false,
|
||||||
}),
|
}),
|
||||||
|
ssl: s
|
||||||
|
.object({
|
||||||
|
key: s.string,
|
||||||
|
cert: s.string,
|
||||||
|
allow_http1: s.boolean.default(false),
|
||||||
|
})
|
||||||
|
.optional.nullish.default(null),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function validate(config): Config {
|
export default function validate(config): Config {
|
||||||
|
|
|
@ -25,7 +25,7 @@ async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promi
|
||||||
return {
|
return {
|
||||||
redirect: discord_auth.oauth_url(
|
redirect: discord_auth.oauth_url(
|
||||||
config.oauth.discord_client_id,
|
config.oauth.discord_client_id,
|
||||||
`${config.core.https ? 'https' : 'http'}://${host}`,
|
`${config.core.return_https ? 'https' : 'http'}://${host}`,
|
||||||
state
|
state
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -35,7 +35,7 @@ async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promi
|
||||||
client_secret: config.oauth.discord_client_secret,
|
client_secret: config.oauth.discord_client_secret,
|
||||||
code,
|
code,
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
redirect_uri: `${config.core.https ? 'https' : 'http'}://${host}/api/auth/oauth/discord`,
|
redirect_uri: `${config.core.return_https ? 'https' : 'http'}://${host}/api/auth/oauth/discord`,
|
||||||
scope: 'identify',
|
scope: 'identify',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promi
|
||||||
return {
|
return {
|
||||||
redirect: google_auth.oauth_url(
|
redirect: google_auth.oauth_url(
|
||||||
config.oauth.google_client_id,
|
config.oauth.google_client_id,
|
||||||
`${config.core.https ? 'https' : 'http'}://${host}`,
|
`${config.core.return_https ? 'https' : 'http'}://${host}`,
|
||||||
state
|
state
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@ async function handler({ code, state, host }: OAuthQuery, logger: Logger): Promi
|
||||||
code,
|
code,
|
||||||
client_id: config.oauth.google_client_id,
|
client_id: config.oauth.google_client_id,
|
||||||
client_secret: config.oauth.google_client_secret,
|
client_secret: config.oauth.google_client_secret,
|
||||||
redirect_uri: `${config.core.https ? 'https' : 'http'}://${host}/api/auth/oauth/google`,
|
redirect_uri: `${config.core.return_https ? 'https' : 'http'}://${host}/api/auth/oauth/google`,
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -58,14 +58,14 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
await sendShorten(
|
await sendShorten(
|
||||||
user,
|
user,
|
||||||
url,
|
url,
|
||||||
`${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}${zconfig.urls.route}/${
|
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}${zconfig.urls.route}/${
|
||||||
req.body.vanity ? req.body.vanity : invis ? invis.invis : url.id
|
req.body.vanity ? req.body.vanity : invis ? invis.invis : url.id
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
url: `${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}${zconfig.urls.route}/${
|
url: `${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}${zconfig.urls.route}/${
|
||||||
req.body.vanity ? req.body.vanity : invis ? invis.invis : url.id
|
req.body.vanity ? req.body.vanity : invis ? invis.invis : url.id
|
||||||
}`,
|
}`,
|
||||||
});
|
});
|
||||||
|
|
|
@ -179,7 +179,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
response.files.push(
|
response.files.push(
|
||||||
`${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}${
|
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}${
|
||||||
zconfig.uploader.route === '/' ? '' : zconfig.uploader.route
|
zconfig.uploader.route === '/' ? '' : zconfig.uploader.route
|
||||||
}/${invis ? invis.invis : file.file}`
|
}/${invis ? invis.invis : file.file}`
|
||||||
);
|
);
|
||||||
|
@ -189,7 +189,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
await sendUpload(
|
await sendUpload(
|
||||||
user,
|
user,
|
||||||
file,
|
file,
|
||||||
`${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}/r/${
|
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}/r/${
|
||||||
invis ? invis.invis : file.file
|
invis ? invis.invis : file.file
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
|
@ -311,7 +311,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
response.files.push(
|
response.files.push(
|
||||||
`${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}${
|
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}${
|
||||||
zconfig.uploader.route === '/' ? '' : zconfig.uploader.route
|
zconfig.uploader.route === '/' ? '' : zconfig.uploader.route
|
||||||
}/${invis ? invis.invis : image.file}`
|
}/${invis ? invis.invis : image.file}`
|
||||||
);
|
);
|
||||||
|
@ -323,7 +323,9 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
||||||
await sendUpload(
|
await sendUpload(
|
||||||
user,
|
user,
|
||||||
image,
|
image,
|
||||||
`${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}/r/${invis ? invis.invis : image.file}`
|
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}/r/${
|
||||||
|
invis ? invis.invis : image.file
|
||||||
|
}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
error: 'oauth token expired',
|
error: 'oauth token expired',
|
||||||
redirect_uri: discord_auth.oauth_url(
|
redirect_uri: discord_auth.oauth_url(
|
||||||
config.oauth.discord_client_id,
|
config.oauth.discord_client_id,
|
||||||
`${config.core.https ? 'https' : 'http'}://${req.headers.host}`
|
`${config.core.return_https ? 'https' : 'http'}://${req.headers.host}`
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
error: 'oauth token expired',
|
error: 'oauth token expired',
|
||||||
redirect_uri: discord_auth.oauth_url(
|
redirect_uri: discord_auth.oauth_url(
|
||||||
config.oauth.discord_client_id,
|
config.oauth.discord_client_id,
|
||||||
`${config.core.https ? 'https' : 'http'}://${req.headers.host}`
|
`${config.core.return_https ? 'https' : 'http'}://${req.headers.host}`
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
error: 'oauth token expired',
|
error: 'oauth token expired',
|
||||||
redirect_uri: google_auth.oauth_url(
|
redirect_uri: google_auth.oauth_url(
|
||||||
config.oauth.google_client_id,
|
config.oauth.google_client_id,
|
||||||
`${config.core.https ? 'https' : 'http'}://${req.headers.host}`
|
`${config.core.return_https ? 'https' : 'http'}://${req.headers.host}`
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
|
||||||
error: 'oauth token expired',
|
error: 'oauth token expired',
|
||||||
redirect_uri: google_auth.oauth_url(
|
redirect_uri: google_auth.oauth_url(
|
||||||
config.oauth.google_client_id,
|
config.oauth.google_client_id,
|
||||||
`${config.core.https ? 'https' : 'http'}://${req.headers.host}`
|
`${config.core.return_https ? 'https' : 'http'}://${req.headers.host}`
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import datasource from '../lib/datasource';
|
||||||
import Logger from '../lib/logger';
|
import Logger from '../lib/logger';
|
||||||
import { getStats } from './util';
|
import { getStats } from './util';
|
||||||
|
|
||||||
import fastify, { FastifyInstance } from 'fastify';
|
import fastify, { FastifyInstance, FastifyServerOptions } from 'fastify';
|
||||||
import { createReadStream, existsSync } from 'fs';
|
import { createReadStream, existsSync, readFileSync } from 'fs';
|
||||||
import dbFileDecorator from './decorators/dbFile';
|
import dbFileDecorator from './decorators/dbFile';
|
||||||
import notFound from './decorators/notFound';
|
import notFound from './decorators/notFound';
|
||||||
import postFileDecorator from './decorators/postFile';
|
import postFileDecorator from './decorators/postFile';
|
||||||
|
@ -24,7 +24,7 @@ import urlsRoute, { urlsRouteOnResponse } from './routes/urls';
|
||||||
const dev = process.env.NODE_ENV === 'development';
|
const dev = process.env.NODE_ENV === 'development';
|
||||||
const logger = Logger.get('server');
|
const logger = Logger.get('server');
|
||||||
|
|
||||||
const server = fastify();
|
const server = fastify(genFastifyOpts());
|
||||||
|
|
||||||
if (dev) {
|
if (dev) {
|
||||||
server.addHook('onRoute', (opts) => {
|
server.addHook('onRoute', (opts) => {
|
||||||
|
@ -197,3 +197,18 @@ async function clearInvites(this: FastifyInstance) {
|
||||||
|
|
||||||
logger.child('invites').debug(`deleted ${count} used invites`);
|
logger.child('invites').debug(`deleted ${count} used invites`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function genFastifyOpts(): FastifyServerOptions {
|
||||||
|
const opts = {};
|
||||||
|
|
||||||
|
if (config.ssl?.cert && config.ssl?.key) {
|
||||||
|
opts['https'] = {
|
||||||
|
key: readFileSync(config.ssl.key),
|
||||||
|
cert: readFileSync(config.ssl.cert),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.ssl?.allow_http1) opts['https']['allowHTTP1'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue