mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
Fixes DB conflict in prod
This commit is contained in:
parent
3d63859412
commit
581393f78e
@ -72,6 +72,7 @@
|
||||
"@loadable/component": "^5.15.0",
|
||||
"@loadable/server": "^5.15.0",
|
||||
"@reduxjs/toolkit": "^1.6.0",
|
||||
"@types/lodash": "^4.14.177",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"axios": "^0.21.1",
|
||||
"chalk": "^4.1.1",
|
||||
@ -92,6 +93,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"morgan": "^1.10.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"os": "^0.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-helmet": "^6.1.0",
|
||||
@ -127,7 +129,6 @@
|
||||
"@types/loadable__component": "^5.13.4",
|
||||
"@types/loadable__server": "^5.12.6",
|
||||
"@types/loadable__webpack-plugin": "^5.7.3",
|
||||
"@types/lodash": "^4.14.175",
|
||||
"@types/mini-css-extract-plugin": "^2.0.1",
|
||||
"@types/morgan": "^1.9.3",
|
||||
"@types/react-dom": "^17.0.8",
|
||||
|
@ -1,7 +1,7 @@
|
||||
export default {
|
||||
HOST: "localhost",
|
||||
PORT: 3000,
|
||||
API_URL: "",
|
||||
API_URL: "http://localhost:3000",
|
||||
GOOGLE_SHEET_ID: "1pMMKcYx6NXLOqNn6pLHJTPMTOLRYZmSNg2QQcAu7-Pw",
|
||||
APP: {
|
||||
htmlAttributes: { lang: "en" },
|
||||
|
@ -1,3 +1,9 @@
|
||||
import os from "os"
|
||||
|
||||
const hostname = os.hostname()
|
||||
|
||||
export default {
|
||||
PORT: 4000,
|
||||
HOST: hostname === "ns3075300" ? "fo.parisestludique.fr" : "localhost",
|
||||
API_URL: hostname === "ns3075300" ? "http://fo.parisestludique.fr" : "http://localhost:4000",
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
const CACHE_RENEW_DELAY = 10000
|
||||
|
||||
export default function DBManager<OperationReturn>(): any {
|
||||
const cache: { [sheetName: string]: any } = {}
|
||||
const cacheTime: { [sheetName: string]: number } = {}
|
||||
|
||||
export default function DBManager<OperationReturn>(sheetName: string): any {
|
||||
type OperationType = "add" | "list" | "set"
|
||||
|
||||
cacheTime[sheetName] = 0
|
||||
|
||||
interface Operation {
|
||||
task: () => Promise<OperationReturn>
|
||||
type: OperationType
|
||||
@ -10,9 +15,6 @@ export default function DBManager<OperationReturn>(): any {
|
||||
reject: (reason: unknown) => void
|
||||
}
|
||||
|
||||
let cache: any
|
||||
let cacheTime = 0
|
||||
|
||||
const operations: Operation[] = []
|
||||
|
||||
async function addDBOperation(type: OperationType, task: () => Promise<OperationReturn>) {
|
||||
@ -37,21 +39,21 @@ export default function DBManager<OperationReturn>(): any {
|
||||
const { task, type, resolve, reject } = operation
|
||||
if (type === "list") {
|
||||
const now = +new Date()
|
||||
if (now < cacheTime + CACHE_RENEW_DELAY) {
|
||||
resolve(cache)
|
||||
if (now < cacheTime[sheetName] + CACHE_RENEW_DELAY) {
|
||||
resolve(cache[sheetName])
|
||||
runNextDBOperation()
|
||||
} else {
|
||||
task()
|
||||
.then((val: OperationReturn) => {
|
||||
cache = val
|
||||
cacheTime = now
|
||||
cache[sheetName] = val
|
||||
cacheTime[sheetName] = now
|
||||
resolve(val)
|
||||
})
|
||||
.catch(reject)
|
||||
.finally(runNextDBOperation)
|
||||
}
|
||||
} else {
|
||||
cacheTime = 0
|
||||
cacheTime[sheetName] = 0
|
||||
task().then(resolve).catch(reject).finally(runNextDBOperation)
|
||||
}
|
||||
}
|
||||
|
@ -5,390 +5,391 @@ import { GoogleSpreadsheet, GoogleSpreadsheetWorksheet } from "google-spreadshee
|
||||
|
||||
import DBManager from "./DBManager"
|
||||
|
||||
const addDBOperation = DBManager()
|
||||
|
||||
const SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
|
||||
const CRED_PATH = path.resolve(process.cwd(), "access/gsheets.json")
|
||||
|
||||
export type ElementWithId = unknown & { id: number }
|
||||
|
||||
export async function listGet<Element extends ElementWithId>(
|
||||
sheetName: string,
|
||||
specimen: Element
|
||||
): Promise<Element[]> {
|
||||
type StringifiedElement = Record<keyof Element, string>
|
||||
return addDBOperation("list", async () => {
|
||||
const sheet = await getGSheet(sheetName)
|
||||
export default function getAccessors<
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
ElementNoId extends object,
|
||||
Element extends ElementNoId & ElementWithId
|
||||
>(sheetName: string, specimen: Element): any {
|
||||
const addDBOperation = DBManager(sheetName)
|
||||
|
||||
// Load sheet into an array of objects
|
||||
const rows = (await sheet.getRows()) as StringifiedElement[]
|
||||
const elements: Element[] = []
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = _.pick(rows[0], Object.keys(specimen)) as Record<keyof Element, string>
|
||||
rows.shift()
|
||||
rows.forEach((row) => {
|
||||
const stringifiedElement = _.pick(row, Object.keys(specimen)) as Record<
|
||||
async function listGet(): Promise<Element[]> {
|
||||
type StringifiedElement = Record<keyof Element, string>
|
||||
return addDBOperation("list", async () => {
|
||||
const sheet = await getGSheet()
|
||||
|
||||
// Load sheet into an array of objects
|
||||
const rows = (await sheet.getRows()) as StringifiedElement[]
|
||||
const elements: Element[] = []
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = _.pick(rows[0], Object.keys(specimen)) as Record<keyof Element, string>
|
||||
rows.shift()
|
||||
rows.forEach((row) => {
|
||||
const stringifiedElement = _.pick(row, Object.keys(specimen)) as Record<
|
||||
keyof Element,
|
||||
string
|
||||
>
|
||||
const element = parseElement(stringifiedElement, types)
|
||||
if (element !== undefined) {
|
||||
elements.push(element)
|
||||
}
|
||||
})
|
||||
|
||||
return elements
|
||||
})
|
||||
}
|
||||
|
||||
async function get(membreId: number): Promise<Element | undefined> {
|
||||
// No need to addDBOperation here, since listGet does it already
|
||||
const list = await listGet()
|
||||
return list.find((element) => element.id === membreId)
|
||||
}
|
||||
|
||||
async function setList(elements: Element[]): Promise<true | undefined> {
|
||||
return addDBOperation("listSet", async () => {
|
||||
const sheet = await getGSheet()
|
||||
|
||||
// Load sheet into an array of objects
|
||||
const rows = await sheet.getRows()
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = _.pick(rows[0], Object.keys(elements[0] || {})) as Record<
|
||||
keyof Element,
|
||||
string
|
||||
>
|
||||
const element = parseElement<Element>(stringifiedElement, types, specimen)
|
||||
if (element !== undefined) {
|
||||
elements.push(element)
|
||||
}
|
||||
})
|
||||
|
||||
return elements
|
||||
})
|
||||
}
|
||||
// Update received rows
|
||||
let rowid = 1
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const element of elements) {
|
||||
const row = rows[rowid]
|
||||
const stringifiedRow = stringifyElement(element, types)
|
||||
|
||||
export async function get<Element extends ElementWithId>(
|
||||
sheetName: string,
|
||||
membreId: number,
|
||||
specimen: Element
|
||||
): Promise<Element | undefined> {
|
||||
// No need to addDBOperation here, since listGet does it already
|
||||
const list = await listGet<Element>(sheetName, specimen)
|
||||
return list.find((element) => element.id === membreId)
|
||||
}
|
||||
|
||||
export async function setList<Element extends ElementWithId>(
|
||||
sheetName: string,
|
||||
elements: Element[]
|
||||
): Promise<true | undefined> {
|
||||
return addDBOperation("listSet", async () => {
|
||||
const sheet = await getGSheet(sheetName)
|
||||
|
||||
// Load sheet into an array of objects
|
||||
const rows = await sheet.getRows()
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = _.pick(rows[0], Object.keys(elements[0] || {})) as Record<
|
||||
keyof Element,
|
||||
string
|
||||
>
|
||||
|
||||
// Update received rows
|
||||
let rowid = 1
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const element of elements) {
|
||||
const row = rows[rowid]
|
||||
const stringifiedRow = stringifyElement(element, types)
|
||||
|
||||
if (!row) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await sheet.addRow(stringifiedRow)
|
||||
} else {
|
||||
const keys = Object.keys(stringifiedRow)
|
||||
const sameCells = _.every(
|
||||
keys,
|
||||
(key: keyof Element) => row[key as string] === stringifiedRow[key]
|
||||
)
|
||||
if (!sameCells) {
|
||||
keys.forEach((key) => {
|
||||
row[key] = stringifiedRow[key as keyof Element]
|
||||
})
|
||||
if (!row) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await row.save()
|
||||
await sheet.addRow(stringifiedRow)
|
||||
} else {
|
||||
const keys = Object.keys(stringifiedRow)
|
||||
const sameCells = _.every(
|
||||
keys,
|
||||
(key: keyof Element) => row[key as string] === stringifiedRow[key]
|
||||
)
|
||||
if (!sameCells) {
|
||||
keys.forEach((key) => {
|
||||
row[key] = stringifiedRow[key as keyof Element]
|
||||
})
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await row.save()
|
||||
}
|
||||
}
|
||||
|
||||
rowid += 1
|
||||
}
|
||||
|
||||
// Delete all following rows
|
||||
for (let rowToDelete = sheet.rowCount - 1; rowToDelete >= rowid; rowToDelete -= 1) {
|
||||
if (rows[rowToDelete]) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await rows[rowToDelete].delete()
|
||||
}
|
||||
}
|
||||
|
||||
rowid += 1
|
||||
}
|
||||
|
||||
// Delete all following rows
|
||||
for (let rowToDelete = sheet.rowCount - 1; rowToDelete >= rowid; rowToDelete -= 1) {
|
||||
if (rows[rowToDelete]) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await rows[rowToDelete].delete()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
export async function set<Element extends ElementWithId>(
|
||||
sheetName: string,
|
||||
element: Element
|
||||
): Promise<Element | undefined> {
|
||||
if (!element) {
|
||||
return undefined
|
||||
return true
|
||||
})
|
||||
}
|
||||
return addDBOperation("set", async () => {
|
||||
const sheet = await getGSheet(sheetName)
|
||||
|
||||
// Load sheet into an array of objects
|
||||
const rows = await sheet.getRows()
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = _.pick(rows[0], Object.keys(element || {})) as Record<keyof Element, string>
|
||||
rows.shift()
|
||||
|
||||
// Replace previous row
|
||||
const stringifiedRow = stringifyElement(element, types)
|
||||
const row = rows.find((rowItem) => +rowItem.id === element.id)
|
||||
if (!row) {
|
||||
async function set(element: Element): Promise<Element | undefined> {
|
||||
if (!element) {
|
||||
return undefined
|
||||
}
|
||||
Object.assign(row, stringifiedRow)
|
||||
await row.save()
|
||||
return element
|
||||
})
|
||||
}
|
||||
return addDBOperation("set", async () => {
|
||||
const sheet = await getGSheet()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export async function add<ElementNoId extends object, Element extends ElementNoId & ElementWithId>(
|
||||
sheetName: string,
|
||||
partialElement: Partial<ElementNoId>
|
||||
): Promise<Element | undefined> {
|
||||
if (!partialElement) {
|
||||
return undefined
|
||||
}
|
||||
return addDBOperation("add", async () => {
|
||||
const sheet = await getGSheet(sheetName)
|
||||
|
||||
// Load sheet into an array of objects
|
||||
const rows = await sheet.getRows()
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = {
|
||||
id: "number",
|
||||
...(_.pick(rows[0], Object.keys(partialElement || {})) as Record<
|
||||
keyof ElementNoId,
|
||||
// Load sheet into an array of objects
|
||||
const rows = await sheet.getRows()
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = _.pick(rows[0], Object.keys(element || {})) as Record<
|
||||
keyof Element,
|
||||
string
|
||||
>),
|
||||
>
|
||||
rows.shift()
|
||||
|
||||
// Replace previous row
|
||||
const stringifiedRow = stringifyElement(element, types)
|
||||
const row = rows.find((rowItem) => +rowItem.id === element.id)
|
||||
if (!row) {
|
||||
return undefined
|
||||
}
|
||||
Object.assign(row, stringifiedRow)
|
||||
await row.save()
|
||||
return element
|
||||
})
|
||||
}
|
||||
|
||||
async function add(partialElement: Partial<ElementNoId>): Promise<Element | undefined> {
|
||||
if (!partialElement) {
|
||||
return undefined
|
||||
}
|
||||
return addDBOperation("add", async () => {
|
||||
const sheet = await getGSheet()
|
||||
|
||||
// Create full element
|
||||
rows.shift()
|
||||
const highestId = rows.reduce((id: number, row) => Math.max(id, +row.id || 0), 0)
|
||||
const element = { id: highestId + 1, ...partialElement } as Element
|
||||
// Load sheet into an array of objects
|
||||
const rows = await sheet.getRows()
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = {
|
||||
id: "number",
|
||||
...(_.pick(rows[0], Object.keys(partialElement || {})) as Record<
|
||||
keyof ElementNoId,
|
||||
string
|
||||
>),
|
||||
}
|
||||
|
||||
// Add element
|
||||
const stringifiedRow = stringifyElement(element, types)
|
||||
await sheet.addRow(stringifiedRow)
|
||||
// Create full element
|
||||
rows.shift()
|
||||
const highestId = rows.reduce((id: number, row) => Math.max(id, +row.id || 0), 0)
|
||||
const element = { id: highestId + 1, ...partialElement } as Element
|
||||
|
||||
return element
|
||||
})
|
||||
}
|
||||
// Add element
|
||||
const stringifiedRow = stringifyElement(element, types)
|
||||
await sheet.addRow(stringifiedRow)
|
||||
|
||||
async function getGSheet(sheetName: string): Promise<GoogleSpreadsheetWorksheet> {
|
||||
const doc = new GoogleSpreadsheet("1pMMKcYx6NXLOqNn6pLHJTPMTOLRYZmSNg2QQcAu7-Pw")
|
||||
const creds = await fs.readFile(CRED_PATH)
|
||||
// Authentication
|
||||
await doc.useServiceAccountAuth(JSON.parse(creds.toString()))
|
||||
await doc.loadInfo()
|
||||
return doc.sheetsByTitle[sheetName]
|
||||
}
|
||||
return element
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function parseElement<Element extends object>(
|
||||
rawElement: Record<keyof Element, string>,
|
||||
types: Record<keyof Element, string>,
|
||||
specimen: Element
|
||||
): Element {
|
||||
const fullElement = _.reduce(
|
||||
types,
|
||||
(element: any, type: string, prop: string) => {
|
||||
const rawProp: string = rawElement[prop as keyof Element]
|
||||
switch (type) {
|
||||
case "string":
|
||||
element[prop] = rawProp
|
||||
break
|
||||
async function getGSheet(): Promise<GoogleSpreadsheetWorksheet> {
|
||||
const doc = new GoogleSpreadsheet("1pMMKcYx6NXLOqNn6pLHJTPMTOLRYZmSNg2QQcAu7-Pw")
|
||||
const creds = await fs.readFile(CRED_PATH)
|
||||
// Authentication
|
||||
await doc.useServiceAccountAuth(JSON.parse(creds.toString()))
|
||||
await doc.loadInfo()
|
||||
return doc.sheetsByTitle[sheetName]
|
||||
}
|
||||
|
||||
case "number":
|
||||
element[prop] = +rawProp
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
element[prop] = rawProp !== "0" && rawProp !== ""
|
||||
break
|
||||
|
||||
case "date":
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const matchDate = rawProp.match(/^([0-9]+)\/([0-9]+)\/([0-9]+)$/)
|
||||
if (matchDate) {
|
||||
element[prop] = new Date(+matchDate[3], +matchDate[2] - 1, +matchDate[1])
|
||||
function parseElement(
|
||||
rawElement: Record<keyof Element, string>,
|
||||
types: Record<keyof Element, string>
|
||||
): Element {
|
||||
const fullElement = _.reduce(
|
||||
types,
|
||||
(element: any, type: string, prop: string) => {
|
||||
const rawProp: string = rawElement[prop as keyof Element]
|
||||
switch (type) {
|
||||
case "string":
|
||||
element[prop] = rawProp
|
||||
break
|
||||
}
|
||||
throw new Error(`Unable to read date from ${rawProp}`)
|
||||
|
||||
default:
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const matchArrayType = type.match(/^(number|string|boolean|date)\[([^\]]+)\]$/)
|
||||
if (!matchArrayType) {
|
||||
throw new Error(`Unknown array type for ${type}`)
|
||||
}
|
||||
if (!rawProp) {
|
||||
element[prop] = []
|
||||
} else {
|
||||
case "number":
|
||||
element[prop] = +rawProp
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
element[prop] = rawProp !== "0" && rawProp !== ""
|
||||
break
|
||||
|
||||
case "date":
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const matchDate = rawProp.match(/^([0-9]+)\/([0-9]+)\/([0-9]+)$/)
|
||||
if (matchDate) {
|
||||
element[prop] = new Date(
|
||||
+matchDate[3],
|
||||
+matchDate[2] - 1,
|
||||
+matchDate[1]
|
||||
)
|
||||
break
|
||||
}
|
||||
throw new Error(`Unable to read date from ${rawProp}`)
|
||||
|
||||
default:
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const matchArrayType = type.match(
|
||||
/^(number|string|boolean|date)\[([^\]]+)\]$/
|
||||
)
|
||||
if (!matchArrayType) {
|
||||
throw new Error(`Unknown array type for ${type}`)
|
||||
}
|
||||
if (!rawProp) {
|
||||
element[prop] = []
|
||||
} else {
|
||||
const arrayType = matchArrayType[1]
|
||||
const delimiter = matchArrayType[2]
|
||||
|
||||
switch (arrayType) {
|
||||
case "string":
|
||||
element[prop] = rawProp.split(delimiter)
|
||||
break
|
||||
|
||||
case "number":
|
||||
element[prop] = _.map(rawProp.split(delimiter), (val) => +val)
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
element[prop] = _.map(
|
||||
rawProp.split(delimiter),
|
||||
(val) => val !== "0" && val !== ""
|
||||
)
|
||||
break
|
||||
|
||||
case "date":
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const rawDates = rawProp.split(delimiter)
|
||||
element[prop] = []
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const rightFormat = rawDates.every((rawDate) => {
|
||||
const matchDateArray = rawDate.match(
|
||||
/^([0-9]+)\/([0-9]+)\/([0-9]+)$/
|
||||
)
|
||||
if (!matchDateArray) {
|
||||
return false
|
||||
}
|
||||
element[prop].push(
|
||||
new Date(
|
||||
+matchDateArray[3],
|
||||
+matchDateArray[2] - 1,
|
||||
+matchDateArray[1]
|
||||
)
|
||||
)
|
||||
return true
|
||||
})
|
||||
if (!rightFormat) {
|
||||
throw new Error(
|
||||
`One array item is not a date in ${rawProp}`
|
||||
)
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unknown array type ${arrayType}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return element
|
||||
},
|
||||
JSON.parse(JSON.stringify(specimen))
|
||||
)
|
||||
return fullElement
|
||||
}
|
||||
|
||||
function stringifyElement(
|
||||
element: Element,
|
||||
types: { id: string } & Record<keyof ElementNoId, string>
|
||||
): Record<keyof Element, string> {
|
||||
const rawElement: Record<keyof Element, string> = _.reduce(
|
||||
types,
|
||||
(stringifiedElement: Record<keyof Element, string>, type: string, prop: string) => {
|
||||
const value = element[prop as keyof Element]
|
||||
switch (type) {
|
||||
case "string":
|
||||
stringifiedElement[prop as keyof Element] = formulaSafe(`${value}`)
|
||||
break
|
||||
|
||||
case "number":
|
||||
stringifiedElement[prop as keyof Element] = `${value}`
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
stringifiedElement[prop as keyof Element] = value ? "X" : ""
|
||||
break
|
||||
|
||||
case "date":
|
||||
stringifiedElement[prop as keyof Element] = stringifiedDate(value)
|
||||
break
|
||||
|
||||
default:
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const matchArrayType = type.match(
|
||||
/^(number|string|boolean|date)\[([^\]]+)\]$/
|
||||
)
|
||||
if (!matchArrayType || !_.isArray(value)) {
|
||||
throw new Error(
|
||||
"Unknown matchArrayType or not an array in stringifyElement"
|
||||
)
|
||||
}
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const arrayType = matchArrayType[1]
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const delimiter = matchArrayType[2]
|
||||
|
||||
switch (arrayType) {
|
||||
case "string":
|
||||
element[prop] = rawProp.split(delimiter)
|
||||
break
|
||||
|
||||
case "number":
|
||||
element[prop] = _.map(rawProp.split(delimiter), (val) => +val)
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
element[prop] = _.map(
|
||||
rawProp.split(delimiter),
|
||||
(val) => val !== "0" && val !== ""
|
||||
if (!_.every(value, _.isString)) {
|
||||
throw new Error(`Each date of ${value} is not a string`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = formulaSafe(
|
||||
value.join(delimiter)
|
||||
)
|
||||
break
|
||||
|
||||
case "date":
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const rawDates = rawProp.split(delimiter)
|
||||
element[prop] = []
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const rightFormat = rawDates.every((rawDate) => {
|
||||
const matchDateArray = rawDate.match(
|
||||
/^([0-9]+)\/([0-9]+)\/([0-9]+)$/
|
||||
)
|
||||
if (!matchDateArray) {
|
||||
return false
|
||||
}
|
||||
element[prop].push(
|
||||
new Date(
|
||||
+matchDateArray[3],
|
||||
+matchDateArray[2] - 1,
|
||||
+matchDateArray[1]
|
||||
)
|
||||
)
|
||||
return true
|
||||
})
|
||||
if (!rightFormat) {
|
||||
throw new Error(`One array item is not a date in ${rawProp}`)
|
||||
case "number":
|
||||
if (!_.every(value, _.isNumber)) {
|
||||
throw new Error(`Each date of ${value} is not a number`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = value.join(delimiter)
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
if (!_.every(value, _.isBoolean)) {
|
||||
throw new Error(`Each date of ${value} is not a boolean`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = _.map(value, (val) =>
|
||||
val ? "X" : ""
|
||||
).join(delimiter)
|
||||
break
|
||||
|
||||
case "date":
|
||||
if (!_.every(value, _.isDate)) {
|
||||
throw new Error(`Each date of ${value} is not a date`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = _.map(
|
||||
value,
|
||||
(val) =>
|
||||
`${val.getDate()}/${
|
||||
val.getMonth() + 1
|
||||
}/${val.getFullYear()}`
|
||||
).join(delimiter)
|
||||
break
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown array type ${arrayType}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return element
|
||||
},
|
||||
JSON.parse(JSON.stringify(specimen))
|
||||
)
|
||||
return fullElement
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function stringifyElement<ElementNoId extends object, Element extends ElementNoId & ElementWithId>(
|
||||
element: Element,
|
||||
types: { id: string } & Record<keyof ElementNoId, string>
|
||||
): Record<keyof Element, string> {
|
||||
const rawElement: Record<keyof Element, string> = _.reduce(
|
||||
types,
|
||||
(stringifiedElement: Record<keyof Element, string>, type: string, prop: string) => {
|
||||
const value = element[prop as keyof Element]
|
||||
switch (type) {
|
||||
case "string":
|
||||
stringifiedElement[prop as keyof Element] = formulaSafe(`${value}`)
|
||||
break
|
||||
return stringifiedElement
|
||||
},
|
||||
JSON.parse(JSON.stringify(element))
|
||||
)
|
||||
|
||||
case "number":
|
||||
stringifiedElement[prop as keyof Element] = `${value}`
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
stringifiedElement[prop as keyof Element] = value ? "X" : ""
|
||||
break
|
||||
|
||||
case "date":
|
||||
stringifiedElement[prop as keyof Element] = stringifiedDate(value)
|
||||
break
|
||||
|
||||
default:
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const matchArrayType = type.match(/^(number|string|boolean|date)\[([^\]]+)\]$/)
|
||||
if (!matchArrayType || !_.isArray(value)) {
|
||||
throw new Error(
|
||||
"Unknown matchArrayType or not an array in stringifyElement"
|
||||
)
|
||||
}
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const arrayType = matchArrayType[1]
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const delimiter = matchArrayType[2]
|
||||
|
||||
switch (arrayType) {
|
||||
case "string":
|
||||
if (!_.every(value, _.isString)) {
|
||||
throw new Error(`Each date of ${value} is not a string`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = formulaSafe(
|
||||
value.join(delimiter)
|
||||
)
|
||||
break
|
||||
|
||||
case "number":
|
||||
if (!_.every(value, _.isNumber)) {
|
||||
throw new Error(`Each date of ${value} is not a number`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = value.join(delimiter)
|
||||
break
|
||||
|
||||
case "boolean":
|
||||
if (!_.every(value, _.isBoolean)) {
|
||||
throw new Error(`Each date of ${value} is not a boolean`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = _.map(value, (val) =>
|
||||
val ? "X" : ""
|
||||
).join(delimiter)
|
||||
break
|
||||
|
||||
case "date":
|
||||
if (!_.every(value, _.isDate)) {
|
||||
throw new Error(`Each date of ${value} is not a date`)
|
||||
}
|
||||
stringifiedElement[prop as keyof Element] = _.map(
|
||||
value,
|
||||
(val) =>
|
||||
`${val.getDate()}/${val.getMonth() + 1}/${val.getFullYear()}`
|
||||
).join(delimiter)
|
||||
break
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown array type ${arrayType}`)
|
||||
}
|
||||
}
|
||||
|
||||
return stringifiedElement
|
||||
},
|
||||
JSON.parse(JSON.stringify(element))
|
||||
)
|
||||
|
||||
return rawElement
|
||||
}
|
||||
|
||||
function formulaSafe(value: string): string {
|
||||
return value.replace(/^=+/, "")
|
||||
}
|
||||
|
||||
function stringifiedDate(value: unknown): string {
|
||||
let date: Date
|
||||
if (value instanceof Date) {
|
||||
date = value
|
||||
} else if (typeof value === "string") {
|
||||
try {
|
||||
date = new Date(value)
|
||||
} catch (e) {
|
||||
throw new Error("Wrong date string format in stringifyElement")
|
||||
}
|
||||
} else {
|
||||
throw new Error("Wrong date format in stringifyElement")
|
||||
return rawElement
|
||||
}
|
||||
return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`
|
||||
|
||||
function formulaSafe(value: string): string {
|
||||
return value.replace(/^=+/, "")
|
||||
}
|
||||
|
||||
function stringifiedDate(value: unknown): string {
|
||||
let date: Date
|
||||
if (value instanceof Date) {
|
||||
date = value
|
||||
} else if (typeof value === "string") {
|
||||
try {
|
||||
date = new Date(value)
|
||||
} catch (e) {
|
||||
throw new Error("Wrong date string format in stringifyElement")
|
||||
}
|
||||
} else {
|
||||
throw new Error("Wrong date format in stringifyElement")
|
||||
}
|
||||
return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`
|
||||
}
|
||||
|
||||
return { listGet, get, setList, set, add }
|
||||
}
|
||||
export { SCOPES }
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { listGetRequest, getRequest, setRequest, addRequest } from "./expressAccessors"
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Envie, EnvieWithoutId } from "../services/envies"
|
||||
|
||||
const sheetName = "Envies d'aider"
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
EnvieWithoutId,
|
||||
Envie
|
||||
>("Envies d'aider", new Envie())
|
||||
|
||||
export const envieListGet = listGetRequest(sheetName, new Envie())
|
||||
export const envieListGet = listGetRequest()
|
||||
|
||||
export const envieGet = getRequest(sheetName, new Envie())
|
||||
export const envieGet = getRequest()
|
||||
|
||||
export const envieAdd = addRequest<EnvieWithoutId, Envie>(sheetName)
|
||||
export const envieAdd = addRequest()
|
||||
|
||||
export const envieSet = setRequest(sheetName)
|
||||
export const envieSet = setRequest()
|
||||
|
@ -1,61 +1,69 @@
|
||||
import { Request, Response, NextFunction } from "express"
|
||||
import { ElementWithId, get, listGet, add, set } from "./accessors"
|
||||
import getAccessors, { ElementWithId } from "./accessors"
|
||||
|
||||
export function getRequest<Element extends { id: number }>(sheetName: string, specimen: Element) {
|
||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const id = parseInt(request.query.id as string, 10) || -1
|
||||
const elements = await get<Element>(sheetName, id, specimen)
|
||||
if (elements) {
|
||||
response.status(200).json(elements)
|
||||
export default function getExpressAccessors<
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
ElementNoId extends object,
|
||||
Element extends ElementNoId & ElementWithId
|
||||
>(sheetName: string, specimen: Element): any {
|
||||
const { get, listGet, add, set } = getAccessors(sheetName, specimen)
|
||||
|
||||
function listGetRequest() {
|
||||
return async (
|
||||
_request: Request,
|
||||
response: Response,
|
||||
_next: NextFunction
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const elements = await listGet()
|
||||
if (elements) {
|
||||
response.status(200).json(elements)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function addRequest<ElementNoId extends object, Element extends ElementNoId & ElementWithId>(
|
||||
sheetName: string
|
||||
) {
|
||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const element = await add<ElementNoId, Element>(sheetName, request.body)
|
||||
if (element) {
|
||||
response.status(200).json(element)
|
||||
function getRequest() {
|
||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const id = parseInt(request.query.id as string, 10) || -1
|
||||
const elements = await get(id)
|
||||
if (elements) {
|
||||
response.status(200).json(elements)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function listGetRequest<Element extends { id: number }>(
|
||||
sheetName: string,
|
||||
specimen: Element
|
||||
) {
|
||||
return async (_request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const elements = await listGet<Element>(sheetName, specimen)
|
||||
if (elements) {
|
||||
response.status(200).json(elements)
|
||||
function addRequest() {
|
||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const element = await add(request.body)
|
||||
if (element) {
|
||||
response.status(200).json(element)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setRequest<Element extends { id: number }>(sheetName: string) {
|
||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const element = await set<Element>(sheetName, request.body)
|
||||
if (element) {
|
||||
response.status(200).json(element)
|
||||
function setRequest() {
|
||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||
try {
|
||||
const element = await set(request.body)
|
||||
if (element) {
|
||||
response.status(200).json(element)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
}
|
||||
|
||||
return { getRequest, addRequest, listGetRequest, setRequest }
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { listGetRequest, getRequest, setRequest, addRequest } from "./expressAccessors"
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { JeuJav, JeuJavWithoutId } from "../services/jeuxJav"
|
||||
|
||||
const sheetName = "Jeux JAV"
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
JeuJavWithoutId,
|
||||
JeuJav
|
||||
>("Jeux JAV", new JeuJav())
|
||||
|
||||
export const jeuJavListGet = listGetRequest(sheetName, new JeuJav())
|
||||
export const jeuJavListGet = listGetRequest()
|
||||
|
||||
export const jeuJavGet = getRequest(sheetName, new JeuJav())
|
||||
export const jeuJavGet = getRequest()
|
||||
|
||||
export const jeuJavAdd = addRequest<JeuJavWithoutId, JeuJav>(sheetName)
|
||||
export const jeuJavAdd = addRequest()
|
||||
|
||||
export const jeuJavSet = setRequest(sheetName)
|
||||
export const jeuJavSet = setRequest()
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { listGetRequest, getRequest, setRequest, addRequest } from "./expressAccessors"
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Membre, MembreWithoutId } from "../services/membres"
|
||||
|
||||
const sheetName = "Membres"
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
MembreWithoutId,
|
||||
Membre
|
||||
>("Membres", new Membre())
|
||||
|
||||
export const membreListGet = listGetRequest(sheetName, new Membre())
|
||||
export const membreListGet = listGetRequest()
|
||||
|
||||
export const membreGet = getRequest(sheetName, new Membre())
|
||||
export const membreGet = getRequest()
|
||||
|
||||
export const membreAdd = addRequest<MembreWithoutId, Membre>(sheetName)
|
||||
export const membreAdd = addRequest()
|
||||
|
||||
export const membreSet = setRequest(sheetName)
|
||||
export const membreSet = setRequest()
|
||||
|
@ -8,7 +8,6 @@ import LoadablePlugin from "@loadable/webpack-plugin"
|
||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"
|
||||
|
||||
export const isDev = process.env.NODE_ENV === "development"
|
||||
|
||||
const getStyleLoaders = (isWeb: boolean, isSass?: boolean) => {
|
||||
let loaders: RuleSetUseItem[] = [
|
||||
{
|
||||
|
@ -53,6 +53,10 @@ const config: Configuration = {
|
||||
},
|
||||
optimization: { minimizer: [new CssMinimizerPlugin()] },
|
||||
plugins: getPlugins(),
|
||||
performance: {
|
||||
maxAssetSize: 512000,
|
||||
maxEntrypointSize: 512000,
|
||||
},
|
||||
}
|
||||
|
||||
export default merge(baseConfig(true), config)
|
||||
|
Loading…
x
Reference in New Issue
Block a user