138 lines
4.0 KiB
TypeScript
138 lines
4.0 KiB
TypeScript
import express from 'express';
|
|
import { version } from '../package.json';
|
|
import { AppDataSource } from './data-source'
|
|
import { Link } from './entities/Link';
|
|
import inferUser from './middleware/inferUser';
|
|
import miscRouter from './routes/miscRoutes';
|
|
import userRouter from './routes/userRoutes';
|
|
import linkRouter from './routes/linkRoutes';
|
|
import { getCorsConfig } from './tools/cors';
|
|
import { LinkService } from './services/linkService';
|
|
import * as env from './tools/env';
|
|
import * as z from 'zod';
|
|
|
|
AppDataSource.initialize().then(async () => {
|
|
|
|
await AppDataSource.runMigrations();
|
|
|
|
const app: express.Express = express();
|
|
const linkService = new LinkService();
|
|
const rs = env.getRewriteStrings();
|
|
|
|
const removedExpired = await linkService.removeAllExpired();
|
|
if (removedExpired !== 0) console.log(`[${Date.now() / 1_000}] (DB) Removed ${removedExpired} expired links.`);
|
|
|
|
app.use(express.json());
|
|
app.use(getCorsConfig());
|
|
app.use(inferUser);
|
|
app.use(miscRouter, userRouter, linkRouter);
|
|
|
|
if (env.getBool('debug', true)) {
|
|
const swaggerJsdocOpts = {
|
|
failOnErrors: true,
|
|
definition: {
|
|
openapi: '3.0.4',
|
|
info: {
|
|
title: 'kittyurl API',
|
|
description: 'A Typescript API for the kittyurl url shortener project.',
|
|
version: version,
|
|
contact: {
|
|
name: 'Git repository for entire project',
|
|
url: 'https://gitea.7o7.cx/kittyteam/kittyurl'
|
|
},
|
|
license: {
|
|
name: 'AGPLv3',
|
|
url: 'https://www.gnu.org/licenses/agpl-3.0.en.html'
|
|
}
|
|
},
|
|
components: {
|
|
securitySchemes: {
|
|
BearerJWT: {
|
|
type: 'http',
|
|
scheme: 'bearer',
|
|
bearerFormat: 'JWT',
|
|
description: 'JWT Authorization header using the Bearer scheme.<br/>Enter your JWT from /api/v1/user/signIn to authorize.'
|
|
}
|
|
}
|
|
},
|
|
security: [
|
|
{ BearerJWT: [] }
|
|
],
|
|
},
|
|
apis: ['./src/routes/*.ts', './src/schemas/*.ts']
|
|
};
|
|
const swaggerUi = require('swagger-ui-express');
|
|
const swaggerJsdoc = require('swagger-jsdoc');
|
|
const swaggerSpec = swaggerJsdoc(swaggerJsdocOpts);
|
|
app.use('/kttydocs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
app.get('/kttydocs.json', (req: express.Request, res: express.Response) => {
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.send(swaggerSpec);
|
|
});
|
|
}
|
|
|
|
// Handle 404s
|
|
// https://stackoverflow.com/a/9802006
|
|
app.use(async function(req: express.Request, res: express.Response) {
|
|
|
|
// Check if host header seems right
|
|
try {
|
|
z.string()
|
|
.includes(rs.fqdn)
|
|
.parse(req.headers.host);
|
|
} catch {
|
|
return res.status(400)
|
|
.json({
|
|
status: 'error',
|
|
error: 'Invalid host. Is your browser sending the host header?',
|
|
code: 'no_host'
|
|
});
|
|
}
|
|
|
|
// Retrieve url, subdomain from request.
|
|
let uri: string = req.url.slice(1); // discards / from /abc, /abc -> abc
|
|
let subdomain: string | null = req.headers.host!.replace(rs.fqdn, '').slice(0, -1) || null; // slice() to remove trailing dot
|
|
|
|
// Try to lookup the url in DB
|
|
const reversedLink: Link | null = await linkService.lookupUriWithExpiryValidation(uri, subdomain);
|
|
|
|
// Found something?
|
|
if (reversedLink !== null) {
|
|
// Count this as a visit
|
|
reversedLink.visits += 1;
|
|
linkService.save(reversedLink);
|
|
|
|
// Redirect the user.
|
|
return res.redirect(302, reversedLink.fullUrl);
|
|
}
|
|
|
|
// Nothing found? Return the standard 404.
|
|
res.status(404);
|
|
if (req.accepts('json')) {
|
|
return res.json({
|
|
status: 'error',
|
|
error: 'Not found',
|
|
code: 'uri_not_found'
|
|
});
|
|
}
|
|
|
|
return res.type('txt')
|
|
.send('Not found');
|
|
});
|
|
|
|
const errorHandler: express.ErrorRequestHandler = (err, req, res, next) => {
|
|
console.error(`[${Date.now() / 1_000}] (ErrorHandler) Server error! Error stack:`);
|
|
console.error(err?.stack);
|
|
return res.status(500)
|
|
.json({
|
|
status: 'error',
|
|
error: 'Server error! Something broke',
|
|
code: 'generic_server_error'
|
|
});
|
|
};
|
|
app.use(errorHandler);
|
|
|
|
app.listen(6567, () => console.log(`[${Date.now() / 1_000}] (HTTP Server) Listening on port 6567.`));
|
|
|
|
}).catch(error => console.log(error))
|