mirror of
https://github.com/sergeyyarkov/asu_tools_service.git
synced 2026-10-11 02:19:09 +03:00
eslint + refactor
This commit is contained in:
parent
4d87c19481
commit
db6de0b2f5
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
uploads
|
uploads/*
|
||||||
.env
|
.env
|
||||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": false,
|
||||||
|
"semi": true
|
||||||
|
}
|
||||||
6
db.js
6
db.js
@ -10,11 +10,11 @@ const connectionConfig = {
|
|||||||
pool: {
|
pool: {
|
||||||
max: 10,
|
max: 10,
|
||||||
min: 0,
|
min: 0,
|
||||||
idleTimeoutMillis: 5000,
|
idleTimeoutMillis: 5000
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
trustServerCertificate: true,
|
trustServerCertificate: true
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const pool = new sql.ConnectionPool(connectionConfig);
|
const pool = new sql.ConnectionPool(connectionConfig);
|
||||||
|
|||||||
14
eslint.config.js
Normal file
14
eslint.config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
import { defineConfig } from "eslint/config";
|
||||||
|
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{
|
||||||
|
files: ["**/*.{js,mjs,cjs}"],
|
||||||
|
plugins: { js },
|
||||||
|
extends: ["js/recommended"],
|
||||||
|
languageOptions: { globals: globals.node }
|
||||||
|
},
|
||||||
|
eslintPluginPrettierRecommended
|
||||||
|
]);
|
||||||
7
index.d.ts
vendored
7
index.d.ts
vendored
@ -1,4 +1,5 @@
|
|||||||
import type http from "node:http";
|
import type http from "node:http";
|
||||||
|
import type IncomingForm from "formidable/Formidable.js";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
type ReportModel = {
|
type ReportModel = {
|
||||||
@ -13,16 +14,16 @@ declare global {
|
|||||||
|
|
||||||
type HTTPRequest = {
|
type HTTPRequest = {
|
||||||
parseJson: () => Promise<any>;
|
parseJson: () => Promise<any>;
|
||||||
req: http.IncomingMessage | null;
|
req: http.IncomingMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
type HTTPResponse = {
|
type HTTPResponse = {
|
||||||
sendJson: (data: Record<string, unknown>, statusCode?: number = 200) => http.ServerResponse;
|
sendJson: (data: Record<string, unknown>, statusCode?: number = 200) => http.ServerResponse;
|
||||||
sendText: (text: string, statusCode?: number = 200) => http.ServerResponse;
|
sendText: (text: string, statusCode?: number = 200) => http.ServerResponse;
|
||||||
res: http.ServerResponse | null;
|
res: http.ServerResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
type HTTPRoute = { method: "POST" | "GET"; handle: HTTPRouteHandler };
|
type HTTPRoute = { method: "POST" | "GET"; handle: HTTPRouteHandler; incomingForm?: IncomingForm };
|
||||||
type HTTPRouteHandler = (ctx: HTTPContext) => Promise<void>;
|
type HTTPRouteHandler = (ctx: HTTPContext) => Promise<void>;
|
||||||
type HTTPRouteMap = Record<string, HTTPRoute>;
|
type HTTPRouteMap = Record<string, HTTPRoute>;
|
||||||
type HTTPContext = { data: any; res: HTTPResponse; req: HTTPRequest };
|
type HTTPContext = { data: any; res: HTTPResponse; req: HTTPRequest };
|
||||||
|
|||||||
18
index.js
18
index.js
@ -2,13 +2,13 @@ import http from "node:http";
|
|||||||
import { routeHandler } from "./router/index.js";
|
import { routeHandler } from "./router/index.js";
|
||||||
import packageJson from "./package.json" with { type: "json" };
|
import packageJson from "./package.json" with { type: "json" };
|
||||||
import { ctxAsyncLocalStorage } from "./context.js";
|
import { ctxAsyncLocalStorage } from "./context.js";
|
||||||
import db from "./db.js";
|
// import db from "./db.js";
|
||||||
import response from "./response.js";
|
import response from "./response.js";
|
||||||
import request from "./request.js";
|
import request from "./request.js";
|
||||||
|
|
||||||
const SERVER_PORT = 3001;
|
const SERVER_PORT = 3000;
|
||||||
|
|
||||||
await db.connect().then(() => console.log("Database connected!"));
|
// await db.connect().then(() => console.log("Database connected!"));
|
||||||
|
|
||||||
http
|
http
|
||||||
.createServer((req, res) => {
|
.createServer((req, res) => {
|
||||||
@ -16,11 +16,19 @@ http
|
|||||||
const url = new URL(`http://localhost:${SERVER_PORT}${req.url}`);
|
const url = new URL(`http://localhost:${SERVER_PORT}${req.url}`);
|
||||||
|
|
||||||
/** @type {HTTPContext} */
|
/** @type {HTTPContext} */
|
||||||
const ctx = { data: null, req: Object.create(request), res: Object.create(response) };
|
const ctx = {
|
||||||
|
data: null,
|
||||||
|
req: Object.create(request),
|
||||||
|
res: Object.create(response)
|
||||||
|
};
|
||||||
ctx.req.req = req;
|
ctx.req.req = req;
|
||||||
ctx.res.res = res;
|
ctx.res.res = res;
|
||||||
|
|
||||||
ctxAsyncLocalStorage.run(ctx, () => routeHandler(url));
|
ctxAsyncLocalStorage.run(ctx, () =>
|
||||||
|
routeHandler(url).then(() => {
|
||||||
|
console.log(`${new Date().toISOString()} Request: ${url.pathname}`);
|
||||||
|
})
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,12 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"lib": ["ESNext"],
|
"lib": ["ESNext"],
|
||||||
|
"strict": true,
|
||||||
"module": "nodenext",
|
"module": "nodenext",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"#root/*": ["./*"]
|
"#root/*": ["./*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
978
package-lock.json
generated
978
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -5,7 +5,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --env-file=.env index.js",
|
"start": "node --env-file=.env index.js",
|
||||||
"dev": "node --env-file=.env --watch index.js"
|
"dev": "node --env-file=.env --watch index.js",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Sergey Yarkov",
|
"author": "Sergey Yarkov",
|
||||||
@ -19,5 +20,13 @@
|
|||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"#root/*": "./*"
|
"#root/*": "./*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
|
"eslint": "^10.0.2",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
|
"globals": "^17.3.0",
|
||||||
|
"prettier": "3.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,5 +15,5 @@ export default {
|
|||||||
});
|
});
|
||||||
this.req.on("error", (err) => reject(err));
|
this.req.on("error", (err) => reject(err));
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,9 @@ export default {
|
|||||||
return this.res.end(JSON.stringify(data, null, 2));
|
return this.res.end(JSON.stringify(data, null, 2));
|
||||||
},
|
},
|
||||||
sendText(text, statusCode = 200) {
|
sendText(text, statusCode = 200) {
|
||||||
this.res.writeHead(statusCode, { "content-type": "text/plain; charset=utf-8" });
|
this.res.writeHead(statusCode, {
|
||||||
|
"content-type": "text/plain; charset=utf-8"
|
||||||
|
});
|
||||||
return this.res.end(text);
|
return this.res.end(text);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import formidable from "formidable";
|
||||||
import { ctxAsyncLocalStorage } from "#root/context.js";
|
import { ctxAsyncLocalStorage } from "#root/context.js";
|
||||||
import handleExcelReportsFileParse from "./reports/parseExcel.handler.js";
|
import { handlers as reportsHandlers } from "./reports/index.js";
|
||||||
import handleReportsSyncParsed from "./reports/syncParsed.handler.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {HTTPRouteMap}
|
* @type {HTTPRouteMap}
|
||||||
@ -8,12 +9,20 @@ import handleReportsSyncParsed from "./reports/syncParsed.handler.js";
|
|||||||
export const ROUTE_MAP = {
|
export const ROUTE_MAP = {
|
||||||
"/api/reports_excel_file_parse": {
|
"/api/reports_excel_file_parse": {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
handle: handleExcelReportsFileParse,
|
handle: reportsHandlers.parseExcelHandler,
|
||||||
|
incomingForm: formidable({
|
||||||
|
maxFields: 1,
|
||||||
|
maxFiles: 1,
|
||||||
|
filename: (_, ext) => `gpp-reports${ext}`,
|
||||||
|
keepExtensions: true,
|
||||||
|
uploadDir: path.join(process.cwd(), "./uploads"),
|
||||||
|
filter: ({ mimetype }) => mimetype && mimetype.includes("application/vnd.ms-excel")
|
||||||
|
})
|
||||||
},
|
},
|
||||||
"/api/reports_sync": {
|
"/api/reports_sync": {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
handle: handleReportsSyncParsed,
|
handle: reportsHandlers.syncParsedHandler
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,6 +41,11 @@ export async function routeHandler(url) {
|
|||||||
ctx.data = await ctx.req.parseJson();
|
ctx.data = await ctx.req.parseJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.req.headers["content-type"]?.includes("multipart/form-data")) {
|
||||||
|
if (!route.incomingForm) throw new Error("Request cannot be processed without defined incoming form.");
|
||||||
|
ctx.data = await route.incomingForm.parse(req.req);
|
||||||
|
}
|
||||||
|
|
||||||
if (route.method === req.req.method) {
|
if (route.method === req.req.method) {
|
||||||
await route.handle(ctx);
|
await route.handle(ctx);
|
||||||
}
|
}
|
||||||
@ -46,10 +60,10 @@ export async function routeHandler(url) {
|
|||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
message: error?.message || "Internal Server Error",
|
message: error?.message || "Internal Server Error",
|
||||||
stack: error?.stack || null,
|
stack: error?.stack || null
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
error?.cause?.statusCode || 500,
|
error?.cause?.statusCode || 500
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
router/reports/index.js
Normal file
4
router/reports/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import parseExcelHandler from "./parseExcel.handler.js";
|
||||||
|
import syncParsedHandler from "./syncParsed.handler.js";
|
||||||
|
|
||||||
|
export const handlers = { parseExcelHandler, syncParsedHandler };
|
||||||
@ -1,6 +1,15 @@
|
|||||||
import path from "node:path";
|
|
||||||
import xlsx from "xlsx";
|
import xlsx from "xlsx";
|
||||||
import formidable from "formidable";
|
|
||||||
|
const sheetName = "Отчеты";
|
||||||
|
|
||||||
|
/** @type {xlsx.ParsingOptions} */
|
||||||
|
const xlsxParseOptions = {
|
||||||
|
sheets: sheetName,
|
||||||
|
cellHTML: false,
|
||||||
|
cellFormula: false,
|
||||||
|
cellDates: true,
|
||||||
|
dateNF: "yyyy-mm-dd"
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Читает Excel файл отчетов и возвращает его в формате JSON
|
* Читает Excel файл отчетов и возвращает его в формате JSON
|
||||||
@ -8,40 +17,18 @@ import formidable from "formidable";
|
|||||||
* @type {HTTPRouteHandler}
|
* @type {HTTPRouteHandler}
|
||||||
*/
|
*/
|
||||||
export default async (ctx) => {
|
export default async (ctx) => {
|
||||||
const { req, res } = ctx;
|
const { res } = ctx;
|
||||||
const sheetName = "Отчеты";
|
const [, files] = ctx.data;
|
||||||
|
const excelFilePath = files?.report?.at(0)?.filepath;
|
||||||
|
|
||||||
/** @type {{ markedReports: ReportModel[], reports: ReportModel[] }} */
|
/** @type {{ markedReports: ReportModel[], reports: ReportModel[] }} */
|
||||||
const ret = { reports: [], markedReports: [] };
|
const ret = { reports: [], markedReports: [] };
|
||||||
|
|
||||||
/** @type {xlsx.ParsingOptions} */
|
|
||||||
const xlsxParseOptions = {
|
|
||||||
sheets: sheetName,
|
|
||||||
cellHTML: false,
|
|
||||||
cellFormula: false,
|
|
||||||
cellDates: true,
|
|
||||||
dateNF: "yyyy-mm-dd",
|
|
||||||
};
|
|
||||||
|
|
||||||
const form = formidable({
|
|
||||||
maxFields: 1,
|
|
||||||
maxFiles: 1,
|
|
||||||
filename: (_, ext) => `gpp-reports${ext}`,
|
|
||||||
keepExtensions: true,
|
|
||||||
uploadDir: path.join(process.cwd(), "./uploads"),
|
|
||||||
filter: ({ mimetype }) => mimetype && mimetype.includes("application/vnd.ms-excel"),
|
|
||||||
});
|
|
||||||
|
|
||||||
const [, files] = await form.parse(req.req);
|
|
||||||
const excelFilePath = files?.report?.at(0)?.filepath;
|
|
||||||
|
|
||||||
if (excelFilePath) {
|
if (excelFilePath) {
|
||||||
const workbook = xlsx.readFile(excelFilePath, xlsxParseOptions);
|
const workbook = xlsx.readFile(excelFilePath, xlsxParseOptions);
|
||||||
const sheet = workbook.Sheets[sheetName];
|
const sheet = workbook.Sheets[sheetName];
|
||||||
|
|
||||||
if (!sheet) {
|
if (!sheet) throw new Error("Invalid reports workbook.", { cause: { statusCode: 400 } });
|
||||||
throw new Error("Invalid reports workbook.", { cause: { statusCode: 400 } });
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = xlsx.utils.sheet_to_json(sheet, { raw: false, blankrows: true });
|
const data = xlsx.utils.sheet_to_json(sheet, { raw: false, blankrows: true });
|
||||||
|
|
||||||
@ -59,24 +46,12 @@ export default async (ctx) => {
|
|||||||
job_description: data[i][cols[4]].split("\r\r\n")[1] || "",
|
job_description: data[i][cols[4]].split("\r\r\n")[1] || "",
|
||||||
root_cause: data[i]["__EMPTY_2"] || "",
|
root_cause: data[i]["__EMPTY_2"] || "",
|
||||||
applicantName: data[i][cols[3]].split("\r\r\n")[0] || "",
|
applicantName: data[i][cols[3]].split("\r\r\n")[0] || "",
|
||||||
executorNames: data[i][cols[4]].split("\r\r\n")[0] || "",
|
executorNames: data[i][cols[4]].split("\r\r\n")[0] || ""
|
||||||
});
|
});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseReasonCallAndJobDesc = (str = "") => {
|
|
||||||
const splitted = splitLongSpacedSentence(str);
|
|
||||||
if (splitted.length > 1) return splitted.slice(1).join(" ");
|
|
||||||
return str.split("\r\n").slice(1).join(" ");
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseNames = (str = "") => {
|
|
||||||
const splitted = splitLongSpacedSentence(str);
|
|
||||||
if (splitted.length > 1) return splitted[0];
|
|
||||||
return str.split("\r\n")[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Остальные отчеты */
|
/** Остальные отчеты */
|
||||||
ret.reports.push({
|
ret.reports.push({
|
||||||
date: data[i][cols[0]],
|
date: data[i][cols[0]],
|
||||||
@ -85,7 +60,7 @@ export default async (ctx) => {
|
|||||||
job_description: parseReasonCallAndJobDesc(data[i][cols[4]]),
|
job_description: parseReasonCallAndJobDesc(data[i][cols[4]]),
|
||||||
root_cause: data[i]["__EMPTY_2"] || "",
|
root_cause: data[i]["__EMPTY_2"] || "",
|
||||||
applicantName: parseNames(data[i][cols[3]]),
|
applicantName: parseNames(data[i][cols[3]]),
|
||||||
executorNames: parseNames(data[i][cols[4]]),
|
executorNames: parseNames(data[i][cols[4]])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -95,16 +70,17 @@ export default async (ctx) => {
|
|||||||
res.sendJson({ data: ret });
|
res.sendJson({ data: ret });
|
||||||
};
|
};
|
||||||
|
|
||||||
// /**
|
function parseReasonCallAndJobDesc(str = "") {
|
||||||
// * @param {string} filepath
|
const splitted = splitLongSpacedSentence(str);
|
||||||
// */
|
if (splitted.length > 1) return splitted.slice(1).join(" ");
|
||||||
// function reportsExcelFileToJson(filepath) {
|
return str.split("\r\n").slice(1).join(" ");
|
||||||
// try {
|
}
|
||||||
|
|
||||||
// } catch (error) {
|
function parseNames(str = "") {
|
||||||
|
const splitted = splitLongSpacedSentence(str);
|
||||||
// }
|
if (splitted.length > 1) return splitted[0];
|
||||||
// }
|
return str.split("\r\n")[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} str
|
* @param {string} str
|
||||||
|
|||||||
@ -7,7 +7,7 @@ var reportItemSchema = yup.object({
|
|||||||
job_description: yup.string().required(),
|
job_description: yup.string().required(),
|
||||||
root_cause: yup.string().required(),
|
root_cause: yup.string().required(),
|
||||||
applicant: yup.number().required(),
|
applicant: yup.number().required(),
|
||||||
executor_ids: yup.array(yup.number()).required(),
|
executor_ids: yup.array(yup.number()).required()
|
||||||
});
|
});
|
||||||
|
|
||||||
/** @type {HTTPRouteHandler} */
|
/** @type {HTTPRouteHandler} */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user