Logger, utils and typescript interfaces, types and enums

This commit is contained in:
2022-12-11 18:18:06 +01:00
parent 57327e63d1
commit b636f86151
17 changed files with 426 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
const path = require("path");
const Field = require("./field.js");
let instance = null;
class Config {
constructor() {
this.location = Config.determineLocation();
this.fields = require(`${this.location}`);
}
static getInstance() {
if (instance == null) {
instance = new Config();
}
return instance;
}
static determineLocation() {
if (process.env.NODE_ENV === "production")
return path.join(__dirname, "../../config/env/production.json");
return path.join(__dirname, "../../config/env/development.json");
}
get(section, option) {
if (
this.fields[section] === undefined ||
this.fields[section][option] === undefined
) {
throw new Error(`Field "${section} => ${option}" does not exist.`);
}
const field = new Field(this.fields[section][option]);
if (field.value === "") {
const envField =
process.env[[section.toUpperCase(), option.toUpperCase()].join("_")];
if (envField !== undefined && envField.length !== 0) {
return envField;
}
}
if (field.value === undefined) {
throw new Error(`${section} => ${option} is empty.`);
}
return field.value;
}
}
module.exports = Config;

View File

@@ -0,0 +1,15 @@
class EnvironmentVariables {
constructor(variables) {
this.variables = variables || process.env;
}
get(variable) {
return this.variables[variable];
}
has(variable) {
return this.get(variable) !== undefined;
}
}
module.exports = EnvironmentVariables;

53
src/config/field.js Normal file
View File

@@ -0,0 +1,53 @@
const Filters = require("./filters.js");
const EnvironmentVariables = require("./environmentVariables.js");
class Field {
constructor(rawValue, environmentVariables) {
this.rawValue = rawValue;
this.filters = new Filters(rawValue);
this.valueWithoutFilters = this.filters.removeFiltersFromValue();
this.environmentVariables = new EnvironmentVariables(environmentVariables);
}
get value() {
if (this.filters.isEmpty()) {
return this.valueWithoutFilters;
}
if (this.filters.has("base64") && !this.filters.has("env")) {
return Field.base64Decode(this.valueWithoutFilters);
}
if (
this.environmentVariables.has(this.valueWithoutFilters) &&
this.environmentVariables.get(this.valueWithoutFilters) === ""
) {
return undefined;
}
if (!this.filters.has("base64") && this.filters.has("env")) {
if (this.environmentVariables.has(this.valueWithoutFilters)) {
return this.environmentVariables.get(this.valueWithoutFilters);
}
return undefined;
}
if (this.filters.has("env") && this.filters.has("base64")) {
if (this.environmentVariables.has(this.valueWithoutFilters)) {
const encodedEnvironmentVariable = this.environmentVariables.get(
this.valueWithoutFilters
);
return Field.base64Decode(encodedEnvironmentVariable);
}
return undefined;
}
return this.valueWithoutFilters;
}
static base64Decode(string) {
return new Buffer(string, "base64").toString("utf-8");
}
}
module.exports = Field;

34
src/config/filters.js Normal file
View File

@@ -0,0 +1,34 @@
class Filters {
constructor(value) {
this.value = value;
this.delimiter = "|";
}
get filters() {
return this.value.split(this.delimiter).slice(0, -1);
}
isEmpty() {
return !this.hasValidType() || this.value.length === 0;
}
has(filter) {
return this.filters.includes(filter);
}
hasValidType() {
return typeof this.value === "string";
}
removeFiltersFromValue() {
if (this.hasValidType() === false) {
return this.value;
}
let filtersCombined = this.filters.join(this.delimiter);
filtersCombined += this.filters.length >= 1 ? this.delimiter : "";
return this.value.replace(filtersCombined, "");
}
}
module.exports = Filters;

24
src/enums/generic.js Normal file
View File

@@ -0,0 +1,24 @@
const GENERIC_STATUS_CODES = {
NON_EXISTANT: "NON_EXISTANT",
NOT_STARTED: "NOT_STARTED",
RESERVED: "RESERVED",
RESERVE_FAILED: "RESERVE_FAILED",
PARTIALLY_CAPTURED: "PARTIALLY_CAPTURED",
CAPTURED: "CAPTURED",
INITIATED: "INITIATED",
CONFIRMED: "CONFIRMED",
COMPLETED: "COMPLETED",
CANCELLED: "CANCELLED",
CANCELLED_CONFLICTING: "CANCELLED_CONFLICTING",
CANCEL_REQUESTED: "CANCEL_REQUESTED",
FAILED: "FAILED",
REFUNDED: "REFUNDED",
REJECTED: "REJECTED",
TIMED_OUT_REJECT: "TIMED_OUT_REJECT",
REFUNDED_PARTIAL: "REFUNDED_PARTIAL",
REFUND_REQUESTED: "REFUND_REQUESTED",
};
module.exports = {
GENERIC_STATUS_CODES,
};

9
src/enums/order.js Normal file
View File

@@ -0,0 +1,9 @@
const { GENERIC_STATUS_CODES } = require(`./generic`);
const ORDER_STATUS_CODES = {
...GENERIC_STATUS_CODES,
};
module.exports = {
ORDER_STATUS_CODES,
};

9
src/enums/vipps.js Normal file
View File

@@ -0,0 +1,9 @@
const { GENERIC_STATUS_CODES } = require(`./generic`);
const VIPPS_STATUS_CODES = {
...GENERIC_STATUS_CODES,
};
module.exports = {
VIPPS_STATUS_CODES,
};

28
src/errors/index.js Normal file
View File

@@ -0,0 +1,28 @@
const WAGO_ERROR_STATUS_CODES = {
CONFLICTING_ORDER_RESERVATION: {
status: "CONFLICTING_ORDER_RESERVATION",
message:
"An order for this product has been already reserved, before this order that is being processed.",
},
UNKNOWN_ERROR: {
status: "UNKNOWN_ERROR",
message: "An unknown error has occured",
},
NO_CAPTURED_PAYMENTS: {
status: "NO_CAPTURED_PAYMENTS",
message: "No captured payments has been found for this order.",
},
};
class WagoError extends Error {
constructor(error = WAGO_ERROR_STATUS_CODES.UNKNOWN_ERROR) {
super(error.message);
this.error = error;
}
}
module.exports = {
WagoError,
WAGO_ERROR_STATUS_CODES,
};

15
src/interfaces/ICart.ts Normal file
View File

@@ -0,0 +1,15 @@
export default interface ICart {
client_id: string;
cart_id: number;
lineitem_id: number;
quantity: number;
sku_id: number;
size: string;
price: number;
product_no: number;
name: string;
description: string;
subtext: string;
image: string;
primary_color: string;
}

View File

@@ -0,0 +1,9 @@
export default interface ICustomer {
city: string;
customer_no?: string;
email: string;
first_name: string;
last_name: string;
street_address: string;
zip_code: number;
}

View File

@@ -0,0 +1,7 @@
export default interface IDeliveryAddress {
firstName: string;
lastName: string;
address: string;
zipCode: string;
city: string;
}

61
src/interfaces/IOrder.ts Normal file
View File

@@ -0,0 +1,61 @@
// import type IProduct from './IProduct';
// import type BadgeType from './BadgeType';
import type ICustomer from "./ICustomer";
export interface IStripePayment {
amount: number;
currency: string;
}
export interface IOrderSummary {
created: Date;
email: string;
first_name: string;
last_name: string;
order_id: string;
order_sum: number;
status: string;
}
export interface IOrder {
customer: ICustomer;
lineItems: ILineItem[];
orderid: string;
shipping: IShipping;
status: string;
updated?: Date;
created?: Date;
}
export interface ILineItem {
sku_id: number;
image: string;
name: string;
price: number;
quantity: number;
size: string;
}
export interface IShipping {
company: string;
tracking_code: string;
tracking_link: string;
user_notified: null;
}
export interface IOrdersLineitem {
orders_lineitem_id: string;
order_id: string;
product_no: number;
product_sku_no: number;
quantity: number;
created?: Date;
updated?: Date;
}
export interface ITracking {
orderId: string;
trackingCode: string;
trackingCompany: string;
trackingLink: string;
}

View File

@@ -0,0 +1,25 @@
export interface IProduct {
product_no: number;
name: string;
subtext?: string;
description?: string;
image?: string;
variation_count?: string;
sum_stock?: number;
updated?: Date;
created?: Date;
}
export interface IProductSku {
sku_id: number;
price: number;
size: number;
stock: number;
default_price: boolean;
updated?: Date;
created?: Date;
}
export interface IProductWithSkus extends IProduct {
variations: IProductSku[] | number;
}

61
src/logger.ts Normal file
View File

@@ -0,0 +1,61 @@
import winston from "winston";
import ecsFormat from "@elastic/ecs-winston-format";
import httpContext from "express-http-context";
const logLevel = "debug";
// const logLevel = 'trace';
const customLevels = {
levels: {
fatal: 0,
error: 1,
warning: 2,
info: 3,
debug: 4,
trace: 5,
},
colors: {
trace: "blue",
debug: "white",
info: "green",
warning: "yellow",
error: "red",
fatal: "red",
},
};
const appendPlanetId = winston.format((log) => {
log.planetId = httpContext.get("planetId");
return log;
});
const appName = winston.format((log) => {
log.application = "planetposen-backend";
return log;
});
const logger = winston.createLogger({
level: logLevel,
levels: customLevels.levels,
transports: [
new winston.transports.File({
filename: "./logs/all-logs.log",
format: winston.format.combine(appendPlanetId(), appName(), ecsFormat()),
}),
],
});
winston.addColors(customLevels.colors);
if (process.env.NODE_ENV !== "production") {
logger.add(
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
})
);
}
export default logger;

11
src/types/global.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
declare global {
interface NodeJS {
__base: string;
__dirname: string;
__enums: string;
__middleware: string;
__controllers: string;
}
}
export {};

View File

@@ -0,0 +1,4 @@
export function validEmail(email: string): boolean {
const emailRegex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;
return emailRegex.test(email);
}

10
src/utils/generateUUID.ts Normal file
View File

@@ -0,0 +1,10 @@
const hex = "0123456789abcdef";
export default function generateClientId(len = 22) {
let output = "";
for (let i = 0; i < len; ++i) {
output += hex.charAt(Math.floor(Math.random() * hex.length));
}
return output;
}