This is way too much time to spend on linters

This commit is contained in:
Gender Shrapnel 2020-08-24 04:08:11 +02:00
parent 69ebed5c7e
commit 0c880a7b4e
23 changed files with 1748 additions and 200 deletions

View File

@ -5,7 +5,10 @@ module.exports = {
},
extends: [
"plugin:react/recommended",
"standard"
"standard",
"prettier",
"prettier/@typescript-eslint",
"prettier/react"
],
parser: "@typescript-eslint/parser",
parserOptions: {
@ -25,25 +28,21 @@ module.exports = {
}
},
rules: {
"space-before-function-paren": ["error", {
anonymous: "always",
named: "never",
asyncArrow: "always"
"no-unused-vars": ["error", {
"vars": "all",
"args": "after-used"
}],
"quote-props": ["error", "consistent"],
"quotes": ["error", "double"],
"sort-imports": ["error", {
"ignoreCase": true,
"allowSeparatedGroups": true
}]
},
overrides: [
{
files: ["*.ts", "*.tsx"],
rules: {
"no-undef": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unused-vars-experimental": "error"
"@typescript-eslint/no-unused-vars-experimental": ["error", {
"ignoreArgsIfArgsAfterAreUsed": true
}]
}
}
]

2
.prettierignore Normal file
View File

@ -0,0 +1,2 @@
dist
node_packages

3
.prettierrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"quoteProps": "consistent"
}

1640
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -56,10 +56,23 @@
"@types/react-router-dom": "^5.1.3",
"@types/webpack-dev-middleware": "^2.0.4",
"@types/yargs": "^13.0.8",
"@typescript-eslint/eslint-plugin": "^3.9.1",
"@typescript-eslint/parser": "^3.9.1",
"css-loader": "^3.4.2",
"eslint": "^7.7.0",
"eslint-config-prettier": "^6.11.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-standard": "^4.0.1",
"html-webpack-plugin": "^4.3.0",
"object-hash": "^2.0.3",
"pg-monitor": "^1.3.1",
"pino-pretty": "^3.6.1",
"prettier": "2.0.5",
"prettier-plugin-organize-imports": "^1.1.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
@ -72,8 +85,6 @@
"ts-node": "^8.8.1",
"ts-node-dev": "^1.0.0-pre.44",
"tsconfig-paths": "^3.9.0",
"tslint": "^5.20.1",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.0.2",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",

View File

@ -18,7 +18,7 @@ export const getTasks = async (
throw new APIError("Invalid response from server.");
}
const count: number = json.count;
const items: Task[] = (json.tasks as any[]).map(t => {
const items: Task[] = (json.tasks as any[]).map((t) => {
if (
typeof t !== "object" ||
!Number.isSafeInteger(t.id) ||
@ -30,7 +30,7 @@ export const getTasks = async (
return {
id: t.id,
name: t.name,
schedule: { type: "Plain" }
schedule: { type: "Plain" },
};
});

View File

@ -2,24 +2,33 @@ import * as React from "react";
import { BrowserRouter as Router, Link, Route, Switch } from "react-router-dom";
import TaskList from "./TaskList";
export default () => <Router>
<div>
<nav>
<ul>
<li><a href="/auth">Login</a></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/">Tasks</Link></li>
</ul>
</nav>
function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<a href="/auth">Login</a>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/">Tasks</Link>
</li>
</ul>
</nav>
<Switch>
<Route path="/about">
About things, yay!
</Route>
<Route path="/">
<TaskList/>
</Route>
</Switch>
</div>
</Router>
<Switch>
<Route path="/about">About things, yay!</Route>
<Route path="/">
<TaskList />
</Route>
</Switch>
</div>
</Router>
);
}
export default App

View File

@ -4,7 +4,7 @@ import { Task, TaskScheduleType } from "@kredens/frontend/store/tasks/types";
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
export default () => {
function TaskList() {
const [taskName, setTaskName] = useState("");
const tasks = useSelector<AppState, { [key: string]: Task }>(state => state.tasks.items);
const dispatch = useDispatch();
@ -37,3 +37,5 @@ export default () => {
</>
);
}
export default TaskList

View File

@ -1,15 +0,0 @@
{
"extends": "../../tslint.json",
"rules": {
"no-implicit-dependencies": [
true,
"dev",
["@kredens/frontend"]
],
"no-submodule-imports": [
true,
"@kredens/frontend",
"redux-saga/effects"
]
}
}

View File

@ -24,7 +24,7 @@ export const getUser = async (req: express.Request) =>
export const authMiddleware: () => express.Handler = () => async (
req,
res,
_res,
next
) => {
if (req.session.userID) {

View File

@ -18,7 +18,7 @@ import {
decodeBase64,
decodeUTF8,
encodeBase64,
encodeUTF8
encodeUTF8,
} from "tweetnacl-util";
const secret = decodeBase64(process.env.SECRET);

View File

@ -18,7 +18,7 @@ import {
MigrationRepository,
SessionRepository,
TaskRepository,
UserRepository
UserRepository,
} from "@kredens/server/db/repos";
import { DateTime } from "luxon";
import pg from "pg";
@ -31,19 +31,19 @@ types.setTypeParser(types.builtins.TIMESTAMP, DateTime.fromSQL);
type ExtendedProtocol = IDatabase<Extensions> & Extensions;
const initOptions: IInitOptions<Extensions> = {
extend(obj: ExtendedProtocol, dc: any) {
extend(obj: ExtendedProtocol) {
obj.migrations = new MigrationRepository(obj, pgp);
obj.tasks = new TaskRepository(obj, pgp);
obj.users = new UserRepository(obj, pgp);
obj.sessions = new SessionRepository(obj, pgp);
}
},
};
const pgp: pgPromise.IMain = pgPromise(initOptions);
if (process.env.NODE_ENV !== "production") {
// tslint:disable-next-line:no-implicit-dependencies
import("pg-monitor").then(monitor => monitor.attach(initOptions));
import("pg-monitor").then((monitor) => monitor.attach(initOptions));
}
const db: ExtendedProtocol = pgp(process.env.DATABASE_URL);

View File

@ -24,7 +24,7 @@ export class LockError extends Error {}
export class MigrationRepository {
private db: IDatabase<any>;
constructor(db: IDatabase<any>, pgp: IMain) {
constructor(db: IDatabase<any>, _pgp: IMain) {
this.db = db;
}
@ -45,15 +45,15 @@ export class MigrationRepository {
public async apply() {
await this.lock();
const applied = (await this.applied()).map(m => m.name);
const applied = (await this.applied()).map((m) => m.name);
const toApply = sql.patches.filter(
p => p.up.isSome() && !applied.find(o => o === p.name)
(p) => p.up.isSome() && !applied.find((o) => o === p.name)
);
for (const patch of toApply) {
logger.info("Applying migration", { name: patch.name });
await patch.up
.map(async qf => {
.map(async (qf) => {
await this.db.none(qf);
await this.db.none(sql.apply, [patch.name]);
})
@ -63,10 +63,10 @@ export class MigrationRepository {
}
public async applied(): Promise<Migration[]> {
return this.db.map(sql.applied, [], row => ({
return this.db.map(sql.applied, [], (row) => ({
appliedAt: row.applied_at as DateTime,
id: +row.id,
name: row.name
name: row.name,
}));
}
}

View File

@ -22,14 +22,14 @@ import { IDatabase, IMain } from "pg-promise";
export class SessionRepository {
private db: IDatabase<any>;
constructor(db: IDatabase<any>, pgp: IMain) {
constructor(db: IDatabase<any>, _pgp: IMain) {
this.db = db;
}
public async all(): Promise<Session[]> {
return this.db.map(sql.all, [], row => ({
return this.db.map(sql.all, [], (row) => ({
session: row.session,
sid: row.sid
sid: row.sid,
}));
}
@ -42,25 +42,25 @@ export class SessionRepository {
}
public async get(sid: string): Promise<Maybe<Session>> {
return this.db.oneOrNone(sql.get, [sid]).then(row =>
return this.db.oneOrNone(sql.get, [sid]).then((row) =>
row
? Maybe.Some({
session: row.session,
sid: row.sid
sid: row.sid,
})
: Maybe.None()
);
}
public async length(): Promise<number> {
return this.db.one(sql.length).then(row => +row.length);
return this.db.one(sql.length).then((row) => +row.length);
}
public async set(sid: string, session: SessionData, expiresAt: DateTime) {
return this.db.none(sql.set, [
sid,
JSON.stringify(session),
expiresAt.toSQL()
expiresAt.toSQL(),
]);
}

View File

@ -13,7 +13,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { ScheduleType, Task } from "@kredens/server/db/models";
import { Task } from "@kredens/server/db/models";
import { tasks as sql } from "@kredens/server/db/sql";
import { IDatabase, IMain } from "pg-promise";
@ -24,14 +24,14 @@ function rowToTask(row: any): Task {
name: row.name,
notes: row.notes,
schedule: row.schedule,
createdAt: row.created_at
createdAt: row.created_at,
};
}
export class TaskRepository {
private db: IDatabase<any>;
constructor(db: IDatabase<any>, pgp: IMain) {
constructor(db: IDatabase<any>, _pgp: IMain) {
this.db = db;
}
@ -40,12 +40,12 @@ export class TaskRepository {
limit?: number,
offset?: number
): Promise<Task[]> {
return this.db.map(sql.list, [owner, limit || 10, offset || 0], row =>
return this.db.map(sql.list, [owner, limit || 10, offset || 0], (row) =>
rowToTask(row)
);
}
public async count(owner: number): Promise<number> {
return this.db.one(sql.count, [owner], row => +row.c);
return this.db.one(sql.count, [owner], (row) => +row.c);
}
}

View File

@ -22,16 +22,16 @@ import { IDatabase, IMain } from "pg-promise";
export class UserRepository {
private db: IDatabase<any>;
constructor(db: IDatabase<any>, pgp: IMain) {
constructor(db: IDatabase<any>, _pgp: IMain) {
this.db = db;
}
public async login(email: string, password: string): Promise<Maybe<number>> {
const { id, encryptedPassword } = await this.db
.oneOrNone(sql.login, [email])
.then(user => ({
.then((user) => ({
encryptedPassword: user.encrypted_password,
id: +user.id
id: +user.id,
}));
if (id === null) {
return None();

View File

@ -13,34 +13,34 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { requireAuthMiddleware } from "@kredens/server/auth";
import { db } from "@kredens/server/db";
import express from "express";
import { db } from "@kredens/server/db"
import express from "express"
import { requireAuthMiddleware } from "@kredens/server/auth"
const router = express.Router();
const router = express.Router()
function tryInt(v: string, def?: number): number | undefined {
if (v) {
const value = parseInt(v, 10);
return isNaN(value) ? def : value;
const value = parseInt(v, 10)
return isNaN(value) ? def : value
}
return def;
return def
}
router.use(requireAuthMiddleware());
router.get("/tasks", async (req, res, next) => {
const limit = tryInt(req.query.limit, 10);
const offset = tryInt(req.query.offset, 0);
router.use(requireAuthMiddleware())
router.get("/tasks", async (req, res) => {
const limit = tryInt(req.query.limit, 10)
const offset = tryInt(req.query.offset, 0)
const result = await db.tx(async tx => {
const tasks = await tx.tasks.list(req.user.id, limit, offset);
const count = await tx.tasks.count(req.user.id);
const tasks = await tx.tasks.list(req.user.id, limit, offset)
const count = await tx.tasks.count(req.user.id)
return {
tasks,
count
};
});
}
})
res.json({
tasks: result.tasks.map(t => ({
@ -51,7 +51,7 @@ router.get("/tasks", async (req, res, next) => {
createdAt: t.createdAt.toISO
})),
count: result.count
});
});
})
})
export default router;
export default router

View File

@ -18,11 +18,11 @@ import express from "express";
const router = express.Router();
router.get("/", async (req, res, next) => {
router.get("/", async (req, res) => {
res.render("login");
});
router.post("/", async (req, res, next) => {
router.post("/", async (req, res) => {
const userID = await db.users.login(req.body.email, req.body.password);
if (userID.isSome()) {
req.session.userID = userID.some();

View File

@ -13,43 +13,43 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { box, unbox } from "@kredens/server/crypto";
import { db } from "@kredens/server/db";
import express from "express";
import createHttpError from "http-errors";
import { DateTime } from "luxon";
import { box, unbox } from "@kredens/server/crypto"
import createHttpError from "http-errors"
import { DateTime } from "luxon"
import { db } from "@kredens/server/db"
import express from "express"
interface Token {
expires: string;
}
const router = express.Router();
const router = express.Router()
router.get("/", async (req, res, next) => {
router.get("/", async (req) => {
const token: Token = {
expires: DateTime.local()
.plus({ hours: 2 })
.toISO()
};
req.log.info("Token issued", { token: box(token) });
});
}
req.log.info("Token issued", { token: box(token) })
})
router.post("/", async (req, res, next) => {
const token: Token = unbox(req.body.token);
const expired = DateTime.fromISO(token.expires).diffNow();
const token: Token = unbox(req.body.token)
const expired = DateTime.fromISO(token.expires).diffNow()
if (expired.as("milliseconds") < 0) {
next(createHttpError(401));
return;
next(createHttpError(401))
return
}
const email: string = req.body.email;
const password: string = req.body.password;
const email: string = req.body.email
const password: string = req.body.password
if (!email || !password || password.length < 8) {
res.send("Please provide an email and a password longer than 8 characters");
return;
res.send("Please provide an email and a password longer than 8 characters")
return
}
await db.users.create(email, password);
});
await db.users.create(email, password)
})
export default router;
export default router

View File

@ -17,7 +17,7 @@ import express from "express";
const router = express.Router();
router.get("/", (req, res, next) => {
router.get("/", (req, res) => {
res.render("index", { title: "Hey", message: "Hi!" });
});

View File

@ -13,9 +13,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Store } from "@holdyourwaffle/express-session";
import { db } from "@kredens/server/db";
import { DateTime, Duration } from "luxon";
import { db } from "@kredens/server/db";
import { Store } from "@holdyourwaffle/express-session";
export class PgStore extends Store {
private ttl: Duration;
@ -30,8 +30,8 @@ export class PgStore extends Store {
) {
db.sessions
.get(sid)
.then(s => cb(null, s.map(ss => ss.session).orNull()))
.catch(r => cb(r, null));
.then((s) => cb(null, s.map((ss) => ss.session).orNull()))
.catch((r) => cb(r, null));
}
public set(sid: string, session: SessionData, cb?: (err?: any) => void) {
@ -39,7 +39,7 @@ export class PgStore extends Store {
const p = db.sessions.set(sid, session, expiresAt);
if (cb) {
p.then(s => cb(null)).catch(r => cb(r));
p.then(() => cb(null)).catch((r) => cb(r));
}
}
@ -47,7 +47,7 @@ export class PgStore extends Store {
const p = db.sessions.destroy(sid);
if (cb) {
p.then(s => cb()).catch(r => cb(r));
p.then(() => cb()).catch((r) => cb(r));
}
}
@ -56,7 +56,7 @@ export class PgStore extends Store {
) {
db.sessions
.all()
.then(ss => {
.then((ss) => {
const sessions: { [sid: string]: SessionData } = {};
for (const s of ss) {
@ -65,27 +65,27 @@ export class PgStore extends Store {
cb(null, sessions);
})
.catch(r => cb(r, null));
.catch((r) => cb(r, null));
}
public length(cb: (err: any, length: number) => void) {
db.sessions
.length()
.then(l => cb(null, l))
.catch(r => cb(r, 0));
.then((l) => cb(null, l))
.catch((r) => cb(r, 0));
}
public clear(cb?: (err?: any) => void) {
const p = db.sessions.clear();
if (cb) {
p.then(() => cb()).catch(r => cb(r));
p.then(() => cb()).catch((r) => cb(r));
}
}
public touch(sid: string, session: SessionData, cb?: (err?: any) => void) {
const p = db.sessions.touch(sid, this.getExpiresAt(session));
if (cb) {
p.then(() => cb()).catch(r => cb(r));
p.then(() => cb()).catch((r) => cb(r));
}
}

View File

@ -1,13 +0,0 @@
{
"extends": "../../tslint.json",
"rules": {
"no-implicit-dependencies": [
true,
["@kredens/server"]
],
"no-submodule-imports": [
true,
"@kredens/server"
]
}
}

View File

@ -1,5 +1,5 @@
import path from "path";
import webpack from "webpack"; // tslint:disable-line:no-implicit-dependencies
import webpack from "webpack";
const config: webpack.Configuration = {
mode: "development",
@ -8,28 +8,28 @@ const config: webpack.Configuration = {
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
publicPath: "/assets/"
publicPath: "/assets/",
},
module: {
rules: [
{
exclude: /node_modules/,
loader: "ts-loader",
test: /\.tsx?$/
}
]
test: /\.tsx?$/,
},
],
},
resolve: {
extensions: [".ts", ".tsx", ".js"],
alias: {
"@kredens": path.resolve(__dirname, "src/")
}
"@kredens": path.resolve(__dirname, "src/"),
},
},
plugins: [
new webpack.DefinePlugin({
API_URL: JSON.stringify("http://localhost:3000/api/")
})
]
API_URL: JSON.stringify("http://localhost:3000/api/"),
}),
],
};
export default config;