wip
This commit is contained in:
parent
c8f19898ae
commit
01e85b5e2f
|
@ -10,6 +10,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^2.21.2",
|
||||
"@types/cron": "^1.7.2",
|
||||
"adm-zip": "^0.5.5",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
|
@ -42,6 +43,7 @@
|
|||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cors": "^2.8.10",
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/express-rate-limit": "^5.1.2",
|
||||
"@types/jsonwebtoken": "^8.5.1",
|
||||
"@types/morgan": "^1.9.2",
|
||||
"@types/node": "^14.14.41",
|
||||
|
@ -315,6 +317,15 @@
|
|||
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/cron": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/cron/-/cron-1.7.2.tgz",
|
||||
"integrity": "sha512-AEpNLRcsVSc5AdseJKNHpz0d4e8+ow+abTaC0fKDbAU86rF1evoFF0oC2fV9FdqtfVXkG2LKshpLTJCFOpyvTg==",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"moment": ">=2.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
|
||||
|
@ -332,6 +343,15 @@
|
|||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-rate-limit": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.2.tgz",
|
||||
"integrity": "sha512-loN1dcr0WEKsbVtXNQKDef4Fmh25prfy+gESrwTDofIhAt17ngTxrsDiEZxLemmfHH279x206CdUB9XHXS9E6Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz",
|
||||
|
@ -398,8 +418,7 @@
|
|||
"node_modules/@types/node": {
|
||||
"version": "14.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz",
|
||||
"integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw=="
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
|
@ -7556,6 +7575,15 @@
|
|||
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/cron": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/cron/-/cron-1.7.2.tgz",
|
||||
"integrity": "sha512-AEpNLRcsVSc5AdseJKNHpz0d4e8+ow+abTaC0fKDbAU86rF1evoFF0oC2fV9FdqtfVXkG2LKshpLTJCFOpyvTg==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"moment": ">=2.14.0"
|
||||
}
|
||||
},
|
||||
"@types/debug": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
|
||||
|
@ -7573,6 +7601,15 @@
|
|||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-rate-limit": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.2.tgz",
|
||||
"integrity": "sha512-loN1dcr0WEKsbVtXNQKDef4Fmh25prfy+gESrwTDofIhAt17ngTxrsDiEZxLemmfHH279x206CdUB9XHXS9E6Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz",
|
||||
|
@ -7639,8 +7676,7 @@
|
|||
"@types/node": {
|
||||
"version": "14.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz",
|
||||
"integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw=="
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^2.21.2",
|
||||
"@types/cron": "^1.7.2",
|
||||
"adm-zip": "^0.5.5",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
|
@ -55,6 +56,7 @@
|
|||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cors": "^2.8.10",
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/express-rate-limit": "^5.1.2",
|
||||
"@types/jsonwebtoken": "^8.5.1",
|
||||
"@types/morgan": "^1.9.2",
|
||||
"@types/node": "^14.14.41",
|
||||
|
|
|
@ -4,7 +4,9 @@ import cors from 'cors';
|
|||
import morgan from 'morgan';
|
||||
import path from 'path';
|
||||
import rfs from 'rotating-file-stream';
|
||||
import ratelimit from 'express-rate-limit';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import jetpack from 'fs-jetpack';
|
||||
import cron from 'cron';
|
||||
// import { loadNuxt, build } from 'nuxt';
|
||||
|
||||
import Routes from './structures/routes';
|
||||
|
@ -12,6 +14,12 @@ import Routes from './structures/routes';
|
|||
const server = express();
|
||||
|
||||
const start = async () => {
|
||||
// Create the folders needed for uploads
|
||||
jetpack.dir('uploads/chunks');
|
||||
jetpack.dir('uploads/thumbs/square');
|
||||
jetpack.dir('uploads/thumbs/preview');
|
||||
|
||||
// Create the server and set it up
|
||||
server.use('trust proxy');
|
||||
server.use(helmet());
|
||||
server.use(cors());
|
||||
|
@ -28,12 +36,6 @@ const start = async () => {
|
|||
return res.status(405).json({ message: 'Incorrect `Accept` header provided' });
|
||||
});
|
||||
|
||||
const rateLimiter = new RateLimit({
|
||||
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW ?? '2000', 10),
|
||||
max: parseInt(process.env.RATE_LIMIT_MAX ?? '5', 10),
|
||||
delayMs: 0
|
||||
});
|
||||
|
||||
// Set up logs for production and dev environments
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const accessLogStream = rfs.createStream('access.log', {
|
||||
|
@ -46,19 +48,28 @@ const start = async () => {
|
|||
}
|
||||
|
||||
// Apply rate limiting to the api only
|
||||
server.use('/api/', rateLimiter);
|
||||
server.use('/api/', rateLimit({
|
||||
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW ?? '2000', 10),
|
||||
max: parseInt(process.env.RATE_LIMIT_MAX ?? '5', 10),
|
||||
message: 'Too many requests from this IP. Slow down dude.'
|
||||
}));
|
||||
|
||||
// Scan and load routes into express
|
||||
await Routes.load(server);
|
||||
server.listen(process.env.port, () => {
|
||||
|
||||
// Listen for incoming connections
|
||||
const listen = server.listen(process.env.port, () => {
|
||||
console.log(`> Chibisafe Server started on port ${process.env.port ?? 5000}.`);
|
||||
});
|
||||
listen.setTimeout(600000);
|
||||
|
||||
if (process.env.nuxtStatic) {
|
||||
server.use(express.static(path.join(__dirname, '..', '..', 'frontend', 'dist')));
|
||||
} else {
|
||||
// void serveNuxt();
|
||||
}
|
||||
// Serve the uploads
|
||||
server.use(express.static(path.join(__dirname, '../../uploads')));
|
||||
|
||||
// void serveNuxt();
|
||||
|
||||
// TODO: move into the database config. (we can just show the crontab line for start, later on we can add dropdowns and stuff)
|
||||
new cron.CronJob('0 0 * * * *', Util.saveStatsToDb, null, true);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "file:../../../database/database.sqlite"
|
||||
shadowDatabaseUrl = "file:../../../database/shadow.sqlite"
|
||||
}
|
||||
|
||||
model albums {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int?
|
||||
name String?
|
||||
zippedAt DateTime?
|
||||
createdAt DateTime?
|
||||
editedAt DateTime?
|
||||
nsfw Boolean? @default(false)
|
||||
|
||||
@@unique([userId, name], name: "albums_userid_name_unique")
|
||||
}
|
||||
|
||||
model albumsFiles {
|
||||
id Int @id @default(autoincrement())
|
||||
albumId Int?
|
||||
fileId Int?
|
||||
|
||||
@@unique([albumId, fileId], name: "albumsfiles_albumid_fileid_unique")
|
||||
}
|
||||
|
||||
model albumsLinks {
|
||||
id Int @id @default(autoincrement())
|
||||
albumId Int?
|
||||
linkId Int? @unique
|
||||
}
|
||||
|
||||
model bans {
|
||||
id Int @id @default(autoincrement())
|
||||
ip String?
|
||||
createdAt DateTime?
|
||||
}
|
||||
|
||||
model fileTags {
|
||||
id Int @id @default(autoincrement())
|
||||
fileId Int?
|
||||
tagId Int?
|
||||
|
||||
@@unique([fileId, tagId], name: "filetags_fileid_tagid_unique")
|
||||
}
|
||||
|
||||
model files {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int?
|
||||
name String?
|
||||
original String?
|
||||
type String?
|
||||
size Int?
|
||||
hash String?
|
||||
ip String?
|
||||
createdAt DateTime?
|
||||
editedAt DateTime?
|
||||
}
|
||||
|
||||
model links {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int?
|
||||
albumId Int?
|
||||
identifier String?
|
||||
views Int?
|
||||
enabled Boolean?
|
||||
enableDownload Boolean?
|
||||
expiresAt DateTime?
|
||||
createdAt DateTime?
|
||||
editedAt DateTime?
|
||||
|
||||
@@unique([userId, albumId, identifier], name: "links_userid_albumid_identifier_unique")
|
||||
}
|
||||
|
||||
model settings {
|
||||
id Int @id @default(autoincrement())
|
||||
key String?
|
||||
value String?
|
||||
}
|
||||
|
||||
model statistics {
|
||||
id Int @id @default(autoincrement())
|
||||
batchId Int?
|
||||
type String?
|
||||
// TODO: This was JSON before so make sure to stringify and parse when saving stats
|
||||
data String?
|
||||
createdAt DateTime?
|
||||
|
||||
@@unique([batchId, type], name: "statistics_batchid_type_unique")
|
||||
}
|
||||
|
||||
model tags {
|
||||
id Int @id @default(autoincrement())
|
||||
uuid String?
|
||||
userId Int?
|
||||
name String?
|
||||
createdAt DateTime?
|
||||
editedAt DateTime?
|
||||
|
||||
@@unique([userId, name], name: "tags_userid_name_unique")
|
||||
}
|
||||
|
||||
model users {
|
||||
id Int @id @default(autoincrement())
|
||||
username String?
|
||||
password String?
|
||||
enabled Boolean?
|
||||
isAdmin Boolean?
|
||||
apiKey String?
|
||||
passwordEditedAt DateTime?
|
||||
apiKeyEditedAt DateTime?
|
||||
createdAt DateTime?
|
||||
editedAt DateTime?
|
||||
|
||||
@@unique([username, apiKey], name: "users_username_apikey_unique")
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
export default prisma;
|
|
@ -0,0 +1,37 @@
|
|||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
stravaLink: string;
|
||||
}
|
||||
|
||||
export interface Segment {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
route: string;
|
||||
stravaLink: string;
|
||||
flagCount: number;
|
||||
removed: boolean;
|
||||
chunkyness: number;
|
||||
wayType: string;
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export interface Rating {
|
||||
id: number;
|
||||
rating: number;
|
||||
comment: string;
|
||||
segmentId: number;
|
||||
}
|
||||
|
||||
export interface Picture {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
path?: string;
|
||||
lat?: string;
|
||||
lng?: string;
|
||||
segmentId: number;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { Application, Request, Response } from 'express';
|
||||
import jetpack from 'fs-jetpack';
|
||||
import path from 'path';
|
||||
|
||||
export default {
|
||||
load: async (server: Application) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
for (const routeFile of await jetpack.findAsync(path.join(__dirname, '..', 'routes'), { matching: '*.{ts,js}' })) {
|
||||
try {
|
||||
const slash = process.platform === 'win32' ? '\\' : '/';
|
||||
const replace = process.env.NODE_ENV === 'production' ? `dist${slash}` : `src${slash}`;
|
||||
const route = await import(routeFile.replace(replace, `..${slash}`));
|
||||
const paths: Array<string> = routeFile.split(slash);
|
||||
const method = paths[paths.length - 1].split('.')[0];
|
||||
|
||||
// Get rid of the filename
|
||||
paths.pop();
|
||||
|
||||
// Get rid of the src/routes part
|
||||
paths.splice(0, 2);
|
||||
|
||||
let routePath: string = paths.join(slash);
|
||||
|
||||
// Transform path variables to express variables
|
||||
routePath = routePath.replace('_', ':');
|
||||
|
||||
// Append the missing /
|
||||
routePath = `/${routePath}`;
|
||||
|
||||
// Build final route
|
||||
const prefix = route.options?.ignoreRoutePrefix ? '' : process.env.routePrefix ?? '';
|
||||
routePath = `${prefix}${routePath}`;
|
||||
|
||||
// Run middlewares if any
|
||||
const middlewares: any[] = [];
|
||||
if (route.middlewares?.length) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
for (const middlewareFile of await jetpack.findAsync(path.join(__dirname, '..', 'middlewares'), { matching: '*.{ts,js}' })) {
|
||||
const middleware = await import(middlewareFile.replace(replace, `..${slash}`));
|
||||
middlewares.push(middleware.default);
|
||||
}
|
||||
}
|
||||
|
||||
// Register the route in Express
|
||||
(server as any)[method](routePath, ...middlewares, (req: Request, res: Response) => route.run(req, res));
|
||||
console.log(`Found route ${method.toUpperCase()} ${routePath}`);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
console.log(`${routeFile} :: ERROR`);
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue