Files
kittyBE/src/app.ts
sherl 681555fef8
All checks were successful
Update changelog / changelog (push) Successful in 25s
fix: fix subdomain retrieval
2026-01-08 13:20:32 +01:00

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))