Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt prettier #392

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .prettierrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
printWidth: 120
semi: false
singleQuote: true
jsxSingleQuote: true
trailingComma: "all"
useTabs: false
tabWidth: 2
bracketSpacing: true
bracketSameLine: false
arrowParens: "always"
endOfLine: "lf"
quoteProps: "as-needed"
plugins:
- "@ianvs/prettier-plugin-sort-imports"
importOrder:
- ^(react|react-router-dom)$
- ""
- <THIRD_PARTY_MODULES>
- ""
- ^(?!.*[.](css|scss|svg)$)[./].*$
- ""
- .(css|scss|svg)$
7 changes: 3 additions & 4 deletions backend/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"rules": {
"@typescript-eslint/quotes": ["error", "single", {"allowTemplateLiterals": true}],
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/semi": ["error", "always"]
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }]
},
"ignorePatterns": ["migrations/*.js"]
}
992 changes: 600 additions & 392 deletions backend/package-lock.json

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
"generate-dummy-content": "NODE_ENV=development node dummy_content_generator.js",
"lint": "npx eslint src",
"test": "jest --coverage",
"test:watch": "jest --watch"
"test:watch": "jest --watch",
"prettier": "prettier --check ./src/",
"prettier:fix": "prettier --write ./src/"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
"prettier": "^3.3.3",
"@types/bcryptjs": "^2.4.2",
"@types/crypto-js": "^4.1.1",
"@types/express": "^4.17.13",
Expand All @@ -32,6 +36,8 @@
"@typescript-eslint/eslint-plugin": "^5.21.0",
"argparse": "^2.0.1",
"eslint": "^8.14.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"image-size": "^1.0.2",
"jest": "^28.1.0",
"ts-jest": "^28.0.3"
Expand All @@ -43,19 +49,19 @@
"crypto-js": "^3.3.0",
"db-migrate": "^1.0.0-beta.18",
"db-migrate-mysql": "^2.2.0",
"dom-serializer": "^2.0.0",
"dotenv-flow": "^3.2.0",
"express": "^4.17.3",
"express-rate-limit": "^6.3.0",
"rate-limiter-flexible": "^3.0.4",
"express-winston": "^4.2.0",
"helmet": "^5.0.2",
"html-escaper": "^3.0.3",
"htmlparser2": "^8.0.0",
"dom-serializer": "^2.0.0",
"joi": "^17.6.0",
"mysql2": "^2.3.3",
"node-fetch": "^2.6.7",
"openai": "^4.11.1",
"rate-limiter-flexible": "^3.0.4",
"redis": "^4.0.6",
"string-strip-html": "^8.3.0",
"trie-search": "^1.3.6",
Expand Down
10 changes: 5 additions & 5 deletions backend/src/CodeError.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export default class CodeError extends Error {
public code: string;
public code: string

constructor(code: string, message: string) {
super(message);
this.code = code;
}
constructor(code: string, message: string) {
super(message)
this.code = code
}
}
146 changes: 74 additions & 72 deletions backend/src/api/ApiMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,106 @@
import express, {RequestHandler} from 'express';
import Joi, {ObjectSchema} from 'joi';
import * as core from 'express-serve-static-core';
import Session from '../session/Session';
import {NextFunction} from 'express-serve-static-core';
import express, { RequestHandler } from 'express'
import * as core from 'express-serve-static-core'
import { NextFunction } from 'express-serve-static-core'
import Joi, { ObjectSchema } from 'joi'

import Session from '../session/Session'

type ResponseSuccess<T> = {
result: 'success';
payload: T;
sync: Date;
};
result: 'success'
payload: T
sync: Date
}
type ResponseError = {
result: 'error';
code: string;
message: string;
meta?: object;
};
type ResponseBody<T> = ResponseSuccess<T> | ResponseError;
result: 'error'
code: string
message: string
meta?: object
}
type ResponseBody<T> = ResponseSuccess<T> | ResponseError

export interface APIRequestHandler<ReqBody, ResPayload, ResBody = ResponseBody<ResPayload>>
extends RequestHandler<core.ParamsDictionary, ResBody, ReqBody>
{
(
req: APIRequest<ReqBody, ResBody>,
res: APIResponse<ResPayload>,
next: NextFunction,
): void;
extends RequestHandler<core.ParamsDictionary, ResBody, ReqBody> {
(req: APIRequest<ReqBody, ResBody>, res: APIResponse<ResPayload>, next: NextFunction): void
}

export interface APIRequest<ReqBody, ResBody = object, P = core.ParamsDictionary> extends express.Request<P, ResBody, ReqBody> {
session: Session;
export interface APIRequest<ReqBody, ResBody = object, P = core.ParamsDictionary>
extends express.Request<P, ResBody, ReqBody> {
session: Session
}
export interface APIResponse<Payload> extends express.Response<ResponseBody<Payload>> {
success(payload: Payload): void;
error(code: string, message: string, status?: number, meta?: object): void;
authRequired(): void;
success(payload: Payload): void
error(code: string, message: string, status?: number, meta?: object): void
authRequired(): void
}

function success<T, Payload = T extends ResponseBody<infer U> ? U : never>(payload: Payload) {
return this.send({
result: 'success',
payload: payload,
sync: new Date()
});
return this.send({
result: 'success',
payload: payload,
sync: new Date(),
})
}

function error(code: string, message: string, status?: number, meta?: object) {
if (status) {
this.status(status);
}
if (status) {
this.status(status)
}

const body: ResponseError = {
result: 'error',
code: code,
message: message
};
const body: ResponseError = {
result: 'error',
code: code,
message: message,
}

if (meta) {
body.meta = meta;
}
if (meta) {
body.meta = meta
}

return this.send(body);
return this.send(body)
}

function authRequired() {
return this.error('auth-required', 'Authorization required');
return this.error('auth-required', 'Authorization required')
}

export function apiMiddleware(): APIRequestHandler<unknown, unknown> {
return (req, res, next) => {
res.success = success;
res.error = error;
res.authRequired = authRequired;
next();
};
return (req, res, next) => {
res.success = success
res.error = error
res.authRequired = authRequired
next()
}
}

export function validate<ReqBody, ResPayload>(schema: ObjectSchema<ReqBody>): APIRequestHandler<ReqBody, ResPayload> {
return (req, res, next) => {
schema.validateAsync(req.body)
.then(result => {
req.body = result;
next();
})
.catch(err => {
res.error('invalid-payload', err.message, 400, {
details: err.details?.map(detail => { return { path: detail.path.join('.'), type: detail.type, error: detail.message }; })
});
});
};
return (req, res, next) => {
schema
.validateAsync(req.body)
.then((result) => {
req.body = result
next()
})
.catch((err) => {
res.error('invalid-payload', err.message, 400, {
details: err.details?.map((detail) => {
return { path: detail.path.join('.'), type: detail.type, error: detail.message }
}),
})
})
}
}

// TODO: have a single source of truth with frontend/src/Conf.ts
export const siteDomainMinLengthChars = 3; // restricted only during creation to allow select special names like "ai"
const siteDomainMaxLengthChars = 15;
const siteNameMinLengthChars = 3;
const siteNameMaxLengthChars = 20;
export const siteDomainMinLengthChars = 3 // restricted only during creation to allow select special names like "ai"
const siteDomainMaxLengthChars = 15
const siteNameMinLengthChars = 3
const siteNameMaxLengthChars = 20

export const joiUsername = Joi.string().regex(/^[a-zа-я0-9_-]{2,30}$/i);
export const joiPassword = Joi.string().min(6);
export const joiFormat = Joi.valid('html', 'source').default('html');
export const joiUsername = Joi.string().regex(/^[a-zа-я0-9_-]{2,30}$/i)
export const joiPassword = Joi.string().min(6)
export const joiFormat = Joi.valid('html', 'source').default('html')

export const joiSite = Joi.string().max(siteDomainMaxLengthChars).regex(/^[a-z\d-]*$/i);
export const joiSiteName = Joi.string().min(siteNameMinLengthChars).max(siteNameMaxLengthChars);
export const joiSite = Joi.string()
.max(siteDomainMaxLengthChars)
.regex(/^[a-z\d-]*$/i)
export const joiSiteName = Joi.string().min(siteNameMinLengthChars).max(siteNameMaxLengthChars)
Loading
Loading