Compare commits
55 Commits
eb3b97240d
...
AFLOW-36-p
Author | SHA1 | Date | |
---|---|---|---|
d359f55f39 | |||
85ccc7523f | |||
c23b1b306c | |||
2371089f88 | |||
6fa2797903 | |||
adc466e09a | |||
af896a6688 | |||
347979bb10 | |||
ddfdfc3092 | |||
56cbebb36b | |||
e307ff97ac | |||
ddb484cac9 | |||
cd37f096ca | |||
f52897fd4d | |||
b0b47e04f8 | |||
660c9c092e | |||
6b092b34b3 | |||
421085a8d5 | |||
ea80b4bf2b | |||
94186a3a18 | |||
db6df2fdc6 | |||
9b2db6eed7 | |||
cdbd4c3c10 | |||
bc9d395e77 | |||
16da321177 | |||
6f7f65fa36 | |||
1605987952 | |||
a79a1eab81 | |||
45a4935190 | |||
ff07698f16 | |||
5aeec6fb28 | |||
3be376b214 | |||
3f55b22ede | |||
534cc3055f | |||
abb7e7bab3 | |||
09e74f9eb6 | |||
720a969484 | |||
5524f14e1a | |||
58a2d2ad19 | |||
c50aa8990c | |||
8d954052f2 | |||
0e4bc7669a | |||
0233453084 | |||
c026b5f1a8 | |||
5d99baea8e | |||
587dac99c5 | |||
45bec04007 | |||
d38713e7ed | |||
5584cc5c41 | |||
57513da827 | |||
185d563ac0 | |||
e0ac509007 | |||
90924aa30d | |||
f4d6ed4d8f | |||
1b7b8af118 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ node_modules
|
||||
config.json
|
||||
dist
|
||||
docs
|
||||
demoData.json
|
@ -13,3 +13,4 @@ LocID_Regal16_Fach7
|
||||
StorageLocation_LocID_Regal16_Fach7
|
||||
|
||||
|
||||
Please also reference our wiki at https://project-name-here.atlassian.net/wiki/spaces/AFLOW/overview
|
@ -10,7 +10,9 @@
|
||||
"/@popperjs/core/dist/umd/popper.min.js.map",
|
||||
"/bootstrap/dist/js/bootstrap.bundle.min.js.map",
|
||||
"/bootstrap-icons/font/fonts/bootstrap-icons.woff",
|
||||
"/tsparticles-confetti/tsparticles.confetti.bundle.min.js"
|
||||
"/tsparticles-confetti/tsparticles.confetti.bundle.min.js",
|
||||
"/bootstrap-table/dist/bootstrap-table.min.js",
|
||||
"/bootstrap-table/dist/bootstrap-table.min.css"
|
||||
],
|
||||
"debugMode": false
|
||||
}
|
||||
|
600
package-lock.json
generated
600
package-lock.json
generated
@ -16,12 +16,16 @@
|
||||
"body-parser": "^1.20.2",
|
||||
"bootstrap": "^5.3.0-alpha3",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
"bootstrap-table": "^1.22.1",
|
||||
"csv": "^6.2.11",
|
||||
"eta": "^2.0.1",
|
||||
"express": "^4.18.2",
|
||||
"express-fileupload": "^1.4.0",
|
||||
"express-session": "^1.17.3",
|
||||
"jquery": "^3.6.4",
|
||||
"lodash": "^4.17.21",
|
||||
"passport": "^0.6.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"signale": "^1.4.0",
|
||||
"tsparticles-confetti": "^2.9.3"
|
||||
},
|
||||
@ -29,7 +33,10 @@
|
||||
"@loancrate/prisma-schema-parser": "^2.0.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/express-fileupload": "^1.4.1",
|
||||
"@types/express-session": "^1.17.7",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/passport": "^1.0.12",
|
||||
"@types/passport-local": "^1.0.35",
|
||||
"@types/signale": "^1.4.4",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
@ -39,6 +46,21 @@
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@antfu/ni": {
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-0.21.4.tgz",
|
||||
"integrity": "sha512-O0Uv9LbLDSoEg26fnMDdDRiPwFJnQSoD4WnrflDwKCJm8Cx/0mV4cGxwBLXan5mGIrpK4Dd7vizf4rQm0QCEAA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"na": "bin/na.mjs",
|
||||
"nci": "bin/nci.mjs",
|
||||
"ni": "bin/ni.mjs",
|
||||
"nlx": "bin/nlx.mjs",
|
||||
"nr": "bin/nr.mjs",
|
||||
"nu": "bin/nu.mjs",
|
||||
"nun": "bin/nun.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.21.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
|
||||
@ -426,16 +448,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/debug": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-4.13.0.tgz",
|
||||
"integrity": "sha512-JrltTewF/paRb5mcM5OvcEi9DtdX0sINOAswruxKQrwOLA7Phqb52OfY38MIzGrsrJ8iUGVqQ5bpYZYpxOGCsQ==",
|
||||
"version": "4.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-4.16.2.tgz",
|
||||
"integrity": "sha512-7L7WbG0qNNZYgLpsVB8rCHCXEyHFyIycRlRDNwkVfjQmACC2OW6AWCYCbfdjQhkF/t7+S3njj8wAWAocSs+Brw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/debug": "4.1.8",
|
||||
"debug": "4.3.4",
|
||||
"strip-ansi": "6.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/debug/node_modules/@types/debug": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
|
||||
"integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/debug/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@ -460,17 +491,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@prisma/engine-core": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engine-core/-/engine-core-4.13.0.tgz",
|
||||
"integrity": "sha512-D+jooGRXKAZcsBanhvFhqXMvNEiNaP+OaOvFOM/eKQQXM0FwtKc27l7mQFv4dd/zOufr5T+fJyrTTQhWyV8oFg==",
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engine-core/-/engine-core-4.6.1.tgz",
|
||||
"integrity": "sha512-JtvdEy9GeGU/xeTYOq3SEN4DiAytHoQty/4pJTZ5vNoGMnu7XF1ToprOCPzyT5oSgm3oQQuwpXMVaebJegwA4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.3.0",
|
||||
"@opentelemetry/sdk-trace-base": "^1.8.0",
|
||||
"@prisma/debug": "4.13.0",
|
||||
"@prisma/engines": "4.13.0",
|
||||
"@prisma/generator-helper": "4.13.0",
|
||||
"@prisma/get-platform": "4.13.0",
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@opentelemetry/sdk-trace-base": "^1.4.0",
|
||||
"@prisma/debug": "4.6.1",
|
||||
"@prisma/engines": "4.6.1",
|
||||
"@prisma/generator-helper": "4.6.1",
|
||||
"@prisma/get-platform": "4.6.1",
|
||||
"chalk": "4.1.2",
|
||||
"execa": "5.1.1",
|
||||
"get-stream": "6.0.1",
|
||||
@ -478,8 +509,63 @@
|
||||
"new-github-issue-url": "0.2.1",
|
||||
"p-retry": "4.6.2",
|
||||
"strip-ansi": "6.0.1",
|
||||
"ts-pattern": "4.2.2",
|
||||
"undici": "5.21.0"
|
||||
"undici": "5.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engine-core/node_modules/@prisma/debug": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-4.6.1.tgz",
|
||||
"integrity": "sha512-BezDvSenTgQDQ6WA3TdTDGcrt0Oh4vmpZtmSOYm1KaSZiSVIL2xT0P9TFM3vtOa4wn7sn/003PyTSxyHS3mShg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/debug": "4.1.7",
|
||||
"debug": "4.3.4",
|
||||
"strip-ansi": "6.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engine-core/node_modules/@prisma/engines": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.6.1.tgz",
|
||||
"integrity": "sha512-3u2/XxvxB+Q7cMXHnKU0CpBiUK1QWqpgiBv28YDo1zOIJE3FCF8DI2vrp6vuwjGt5h0JGXDSvmSf4D4maVjJdw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engine-core/node_modules/@prisma/generator-helper": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-4.6.1.tgz",
|
||||
"integrity": "sha512-70XBmqDhmpe8H35ttOJOgyg1OpppO/uelILB1SIwjeSI7PHHdU2+Y/+LkpnifkCEpSZKIhxEIPbHx17m2neAsA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "4.6.1",
|
||||
"@types/cross-spawn": "6.0.2",
|
||||
"chalk": "4.1.2",
|
||||
"cross-spawn": "7.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engine-core/node_modules/@prisma/get-platform": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-4.6.1.tgz",
|
||||
"integrity": "sha512-JBlzN53Q00bTfk3mPxeprAx8LLN7bmEwTGZ3fFjbCKZACsHtbDaaqtIkqXwk0tv1jJ3jLYZfcq7NlvdOPyJhGw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "4.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engine-core/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engine-core/node_modules/indent-string": {
|
||||
@ -491,16 +577,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engine-core/node_modules/ts-pattern": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.2.2.tgz",
|
||||
"integrity": "sha512-qzJMo2pbkUJWusRH5o8xR+xogn6RmvViyUgwBFTtRENLse470clCGjHDf6haWGZ1AOmk8XkEohUoBW8Uut6Scg==",
|
||||
"node_modules/@prisma/engine-core/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.13.0.tgz",
|
||||
"integrity": "sha512-HrniowHRZXHuGT9XRgoXEaP2gJLXM5RMoItaY2PkjvuZ+iHc0Zjbm/302MB8YsPdWozAPHHn+jpFEcEn71OgPw==",
|
||||
"version": "4.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.2.tgz",
|
||||
"integrity": "sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
@ -510,21 +596,21 @@
|
||||
"integrity": "sha512-3jum8/YSudeSN0zGW5qkpz+wAN2V/NYCQ+BPjvHYDfWatLWlQkqy99toX0GysDeaUoBIJg1vaz2yKqiA3CFcQw=="
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-4.13.0.tgz",
|
||||
"integrity": "sha512-khMMm3I8nMUgwd7DoM1wBFjMKn/Z5PZF27EMQGPtquM1nRt601blJbep0qodXR7oHioQolCK8gfbw5Hfdml1mg==",
|
||||
"version": "4.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-4.16.2.tgz",
|
||||
"integrity": "sha512-lnCnHcOaNn0kw8qTJbVcNhyfIf5Lus2GFXbj3qpkdKEIB9xLgqkkuTP+35q1xFaqwQ0vy4HFpdRUpFP7njE15g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "4.13.0",
|
||||
"@prisma/get-platform": "4.13.0",
|
||||
"chalk": "4.1.2",
|
||||
"@prisma/debug": "4.16.2",
|
||||
"@prisma/get-platform": "4.16.2",
|
||||
"execa": "5.1.1",
|
||||
"find-cache-dir": "3.3.2",
|
||||
"fs-extra": "11.1.1",
|
||||
"hasha": "5.2.2",
|
||||
"http-proxy-agent": "5.0.0",
|
||||
"https-proxy-agent": "5.0.1",
|
||||
"node-fetch": "2.6.9",
|
||||
"http-proxy-agent": "7.0.0",
|
||||
"https-proxy-agent": "7.0.0",
|
||||
"kleur": "4.1.5",
|
||||
"node-fetch": "2.6.11",
|
||||
"p-filter": "2.1.0",
|
||||
"p-map": "4.0.0",
|
||||
"p-retry": "4.6.2",
|
||||
@ -534,34 +620,95 @@
|
||||
"tempy": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/generator-helper": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-4.13.0.tgz",
|
||||
"integrity": "sha512-Zx9rtbsEIU/9DKnGRvmjLdyeyhzeeXNFp1alkRh1w//rQ5jTcobiay/jU4Qo+EQOs2bnWEfUeiBoOjfaysdLNg==",
|
||||
"node_modules/@prisma/fetch-engine/node_modules/agent-base": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
|
||||
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "4.13.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine/node_modules/http-proxy-agent": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
|
||||
"integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine/node_modules/https-proxy-agent": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz",
|
||||
"integrity": "sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"agent-base": "^7.0.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@prisma/generator-helper": {
|
||||
"version": "4.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-4.16.2.tgz",
|
||||
"integrity": "sha512-bMOH7y73Ui7gpQrioFeavMQA+Tf8ksaVf8Nhs9rQNzuSg8SSV6E9baczob0L5KGZTSgYoqnrRxuo03kVJYrnIg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "4.16.2",
|
||||
"@types/cross-spawn": "6.0.2",
|
||||
"chalk": "4.1.2",
|
||||
"cross-spawn": "7.0.3"
|
||||
"cross-spawn": "7.0.3",
|
||||
"kleur": "4.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/get-platform": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-4.13.0.tgz",
|
||||
"integrity": "sha512-HWBgQNpRi1qEXj24w/qsWCpTW4vKP2/pdiYTyl2SSEVoK4o5izHUt0PQzNl5St5CGr4SDcSyi0C3fXINd46c6A==",
|
||||
"version": "4.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-4.16.2.tgz",
|
||||
"integrity": "sha512-fnDey1/iSefHJRMB+w243BhWENf+paRouPMdCqIVqu8dYkR1NqhldblsSUC4Zr2sKS7Ta2sK4OLdt9IH+PZTfw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "4.13.0",
|
||||
"chalk": "4.1.2",
|
||||
"@prisma/debug": "4.16.2",
|
||||
"escape-string-regexp": "4.0.0",
|
||||
"execa": "5.1.1",
|
||||
"fs-jetpack": "5.1.0",
|
||||
"kleur": "4.1.5",
|
||||
"replace-string": "3.1.0",
|
||||
"strip-ansi": "6.0.1",
|
||||
"tempy": "1.0.1",
|
||||
"terminal-link": "2.1.1",
|
||||
"ts-pattern": "4.2.2"
|
||||
"ts-pattern": "4.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/get-platform/node_modules/escape-string-regexp": {
|
||||
@ -576,53 +723,46 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/get-platform/node_modules/ts-pattern": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.2.2.tgz",
|
||||
"integrity": "sha512-qzJMo2pbkUJWusRH5o8xR+xogn6RmvViyUgwBFTtRENLse470clCGjHDf6haWGZ1AOmk8XkEohUoBW8Uut6Scg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@prisma/internals": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/internals/-/internals-4.13.0.tgz",
|
||||
"integrity": "sha512-TxdTQFJO0zMCx0Tgb6UZt3WsVt+etvtWx+LaSWf4Pk1ECGte5w+9iiuypwjiLortSn7qm6n5KVWTXFOnnZr4Ww==",
|
||||
"version": "4.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/internals/-/internals-4.16.2.tgz",
|
||||
"integrity": "sha512-/3OiSADA3RRgsaeEE+MDsBgL6oAMwddSheXn6wtYGUnjERAV/BmF5bMMLnTykesQqwZ1s8HrISrJ0Vf6cjOxMg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "4.13.0",
|
||||
"@prisma/engine-core": "4.13.0",
|
||||
"@prisma/engines": "4.13.0",
|
||||
"@prisma/fetch-engine": "4.13.0",
|
||||
"@prisma/generator-helper": "4.13.0",
|
||||
"@prisma/get-platform": "4.13.0",
|
||||
"@prisma/ni": "4.13.0",
|
||||
"@prisma/prisma-fmt-wasm": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a",
|
||||
"@antfu/ni": "0.21.4",
|
||||
"@opentelemetry/api": "1.4.1",
|
||||
"@prisma/debug": "4.16.2",
|
||||
"@prisma/engines": "4.16.2",
|
||||
"@prisma/fetch-engine": "4.16.2",
|
||||
"@prisma/generator-helper": "4.16.2",
|
||||
"@prisma/get-platform": "4.16.2",
|
||||
"@prisma/prisma-fmt-wasm": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81",
|
||||
"archiver": "5.3.1",
|
||||
"arg": "5.0.2",
|
||||
"chalk": "4.1.2",
|
||||
"checkpoint-client": "1.1.23",
|
||||
"checkpoint-client": "1.1.24",
|
||||
"cli-truncate": "2.1.0",
|
||||
"dotenv": "16.0.3",
|
||||
"escape-string-regexp": "4.0.0",
|
||||
"execa": "5.1.1",
|
||||
"find-up": "5.0.0",
|
||||
"fp-ts": "2.13.1",
|
||||
"fp-ts": "2.16.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"fs-jetpack": "5.1.0",
|
||||
"global-dirs": "3.0.1",
|
||||
"globby": "11.1.0",
|
||||
"has-yarn": "2.1.0",
|
||||
"is-windows": "^1.0.2",
|
||||
"is-wsl": "^2.2.0",
|
||||
"indent-string": "4.0.0",
|
||||
"is-windows": "1.0.2",
|
||||
"is-wsl": "2.2.0",
|
||||
"kleur": "4.1.5",
|
||||
"new-github-issue-url": "0.2.1",
|
||||
"node-fetch": "2.6.9",
|
||||
"node-fetch": "2.6.11",
|
||||
"npm-packlist": "5.1.3",
|
||||
"open": "7",
|
||||
"ora": "5.4.1",
|
||||
"open": "7.4.2",
|
||||
"p-map": "4.0.0",
|
||||
"prompts": "2.4.2",
|
||||
"read-pkg-up": "7.0.1",
|
||||
"replace-string": "3.1.0",
|
||||
"resolve": "1.22.1",
|
||||
"resolve": "1.22.2",
|
||||
"string-width": "4.2.3",
|
||||
"strip-ansi": "6.0.1",
|
||||
"strip-indent": "3.0.0",
|
||||
@ -631,7 +771,7 @@
|
||||
"tempy": "1.0.1",
|
||||
"terminal-link": "2.1.1",
|
||||
"tmp": "0.2.1",
|
||||
"ts-pattern": "^4.0.1"
|
||||
"ts-pattern": "4.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/internals/node_modules/escape-string-regexp": {
|
||||
@ -646,16 +786,42 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/ni": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/ni/-/ni-4.13.0.tgz",
|
||||
"integrity": "sha512-FN1wV1I61P6WndheLpja5oQuzIW2WkYqFViPL5GS5dhrpsO69SlyOnAsxpfCKUa5HhXINi7fowgdcngK1QDW9w==",
|
||||
"node_modules/@prisma/internals/node_modules/fp-ts": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.0.tgz",
|
||||
"integrity": "sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@prisma/internals/node_modules/indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/internals/node_modules/resolve": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.11.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/prisma-fmt-wasm": {
|
||||
"version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/prisma-fmt-wasm/-/prisma-fmt-wasm-4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a.tgz",
|
||||
"integrity": "sha512-kEYSUa3XT1Oiu/MbdUkyjfVtAOQmZz69KGKFH/GWoQNLvkscrqy4J4XewEY80BrVuyC3vbV7un4kea0xklWhpA==",
|
||||
"version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/prisma-fmt-wasm/-/prisma-fmt-wasm-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz",
|
||||
"integrity": "sha512-g090+dEH7wrdCw359+8J9+TGH84qK28V/dxwINjhhNCtju9lej99z9w/AVsJP9UhhcCPS4psYz4iu8d53uxVpA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@sentry-internal/tracing": {
|
||||
@ -831,6 +997,15 @@
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-session": {
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.7.tgz",
|
||||
"integrity": "sha512-L25080PBYoRLu472HY/HNCxaXY8AaGgqGC8/p/8+BYMhG0RDOLQ1wpXOpAzr4Gi5TGozTKyJv5BVODM5UNyVMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.194",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz",
|
||||
@ -867,6 +1042,36 @@
|
||||
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/passport": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz",
|
||||
"integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/passport-local": {
|
||||
"version": "1.0.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.35.tgz",
|
||||
"integrity": "sha512-K4eLTJ8R0yYW8TvCqkjB0pTKoqfUSdl5PfZdidTjV2ETV3604fQxtY6BHKjQWAx50WUS0lqzBvKv3LoI1ZBPeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/express": "*",
|
||||
"@types/passport": "*",
|
||||
"@types/passport-strategy": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/passport-strategy": {
|
||||
"version": "0.2.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
|
||||
"integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/express": "*",
|
||||
"@types/passport": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
|
||||
@ -1299,6 +1504,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/bootstrap-table": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.22.1.tgz",
|
||||
"integrity": "sha512-Nw8p+BmaiMDSfoer/p49YeI3vJQAWhudxhyKMuqnJBb3NRvCRewMk7JDgiN9SQO3YeSejOirKtcdWpM0dtddWg==",
|
||||
"peerDependencies": {
|
||||
"jquery": "3"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@ -1443,18 +1656,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/checkpoint-client": {
|
||||
"version": "1.1.23",
|
||||
"resolved": "https://registry.npmjs.org/checkpoint-client/-/checkpoint-client-1.1.23.tgz",
|
||||
"integrity": "sha512-NrGvMiH1fUcZwrYc0Z+YTn7q9ysV0kPgVyDKZ5jrfIerFJuSllvyGsY7bHeQSEiljaIaUP1Q/xutZ8q1s7PGzg==",
|
||||
"version": "1.1.24",
|
||||
"resolved": "https://registry.npmjs.org/checkpoint-client/-/checkpoint-client-1.1.24.tgz",
|
||||
"integrity": "sha512-nIOlLhDS7MKs4tUzS3LCm+sE1NgTCVnVrXlD0RRxaoEkkLu8LIWSUNiNWai6a+LK5unLzTyZeTCYX1Smqy0YoA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ci-info": "3.3.0",
|
||||
"ci-info": "3.8.0",
|
||||
"env-paths": "2.2.1",
|
||||
"fast-write-atomic": "0.2.1",
|
||||
"make-dir": "3.1.0",
|
||||
"ms": "2.1.3",
|
||||
"node-fetch": "2.6.7",
|
||||
"uuid": "8.3.2"
|
||||
"node-fetch": "2.6.11",
|
||||
"uuid": "9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/checkpoint-client/node_modules/ci-info": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/sibiraj-s"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/checkpoint-client/node_modules/ms": {
|
||||
@ -1463,24 +1691,13 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/checkpoint-client/node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"node_modules/checkpoint-client/node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
@ -2328,6 +2545,32 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session": {
|
||||
"version": "1.17.3",
|
||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
|
||||
"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
|
||||
"dependencies": {
|
||||
"cookie": "0.4.2",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-headers": "~1.0.2",
|
||||
"parseurl": "~1.3.3",
|
||||
"safe-buffer": "5.2.1",
|
||||
"uid-safe": "~2.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session/node_modules/cookie": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
@ -4007,9 +4250,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||
"integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
|
||||
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
@ -4042,9 +4285,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-package-data/node_modules/semver": {
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
|
||||
"integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@ -4202,6 +4445,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@ -4413,6 +4664,42 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/passport": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
|
||||
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x",
|
||||
"pause": "0.0.1",
|
||||
"utils-merge": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jaredhanson"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-local": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
||||
"integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@ -4460,6 +4747,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pause": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
@ -4711,28 +5003,6 @@
|
||||
"strip-ansi": "6.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma-dbml-generator/node_modules/@prisma/engine-core": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engine-core/-/engine-core-4.6.1.tgz",
|
||||
"integrity": "sha512-JtvdEy9GeGU/xeTYOq3SEN4DiAytHoQty/4pJTZ5vNoGMnu7XF1ToprOCPzyT5oSgm3oQQuwpXMVaebJegwA4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@opentelemetry/sdk-trace-base": "^1.4.0",
|
||||
"@prisma/debug": "4.6.1",
|
||||
"@prisma/engines": "4.6.1",
|
||||
"@prisma/generator-helper": "4.6.1",
|
||||
"@prisma/get-platform": "4.6.1",
|
||||
"chalk": "4.1.2",
|
||||
"execa": "5.1.1",
|
||||
"get-stream": "6.0.1",
|
||||
"indent-string": "4.0.0",
|
||||
"new-github-issue-url": "0.2.1",
|
||||
"p-retry": "4.6.2",
|
||||
"strip-ansi": "6.0.1",
|
||||
"undici": "5.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma-dbml-generator/node_modules/@prisma/engines": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.6.1.tgz",
|
||||
@ -4923,15 +5193,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma-dbml-generator/node_modules/indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma-dbml-generator/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@ -4958,18 +5219,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/prisma-dbml-generator/node_modules/undici": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.11.0.tgz",
|
||||
"integrity": "sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"busboy": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.18"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma-docs-generator": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma-docs-generator/-/prisma-docs-generator-0.7.0.tgz",
|
||||
@ -5107,6 +5356,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@ -5241,9 +5498,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/read-pkg/node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
@ -5488,9 +5745,9 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -6056,9 +6313,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-pattern": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.2.3.tgz",
|
||||
"integrity": "sha512-tPg2/owaVtWiimsmXpFEzI5IcfPU2BEwzFbviuSmqqaKIGyy6hyvBF4kxcuhy8UJz+6nEKUOEeaHc43drIuvpQ==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.3.0.tgz",
|
||||
"integrity": "sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ts-toolbelt": {
|
||||
@ -6360,6 +6617,17 @@
|
||||
"node": ">=12.20"
|
||||
}
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||
"dependencies": {
|
||||
"random-bytes": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
||||
@ -6376,9 +6644,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.21.0.tgz",
|
||||
"integrity": "sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA==",
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.11.0.tgz",
|
||||
"integrity": "sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"busboy": "^1.6.0"
|
||||
@ -6543,9 +6811,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
@ -24,12 +24,16 @@
|
||||
"body-parser": "^1.20.2",
|
||||
"bootstrap": "^5.3.0-alpha3",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
"bootstrap-table": "^1.22.1",
|
||||
"csv": "^6.2.11",
|
||||
"eta": "^2.0.1",
|
||||
"express": "^4.18.2",
|
||||
"express-fileupload": "^1.4.0",
|
||||
"express-session": "^1.17.3",
|
||||
"jquery": "^3.6.4",
|
||||
"lodash": "^4.17.21",
|
||||
"passport": "^0.6.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"signale": "^1.4.0",
|
||||
"tsparticles-confetti": "^2.9.3"
|
||||
},
|
||||
@ -37,7 +41,10 @@
|
||||
"@loancrate/prisma-schema-parser": "^2.0.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/express-fileupload": "^1.4.1",
|
||||
"@types/express-session": "^1.17.7",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/passport": "^1.0.12",
|
||||
"@types/passport-local": "^1.0.35",
|
||||
"@types/signale": "^1.4.4",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
|
@ -32,12 +32,13 @@ enum itemStatus {
|
||||
lost
|
||||
}
|
||||
|
||||
// comments and descriptions -> @db.VarChar(2048)
|
||||
model Item {
|
||||
id Int @id @unique @default(autoincrement())
|
||||
SKU String? @unique
|
||||
amount Int @default(1)
|
||||
name String
|
||||
comment String?
|
||||
comment String? @db.VarChar(2048)
|
||||
status itemStatus @default(normal) /// TODO: Would it be better to create a separate model for this as well instead of providing several static statuses to choose from(enum)?
|
||||
|
||||
contactInfo contactInfo? @relation(fields: [contactInfoId], references: [id])
|
||||
@ -49,7 +50,7 @@ model Item {
|
||||
categoryId Int?
|
||||
|
||||
contents Item[] @relation("items") /// Item beinhaltet..
|
||||
baseItem Item[] @relation("items") /// Item zugehörig zu.
|
||||
baseItem Item[] @relation("items") /// Item zugehörig zu
|
||||
|
||||
storageLocation StorageLocation? @relation(fields: [storageLocationId], references: [id])
|
||||
storageLocationId Int?
|
||||
@ -81,7 +82,7 @@ model StorageUnit {
|
||||
model itemCategory {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
description String?
|
||||
description String? @db.VarChar(2048)
|
||||
Item Item[]
|
||||
}
|
||||
|
||||
@ -98,6 +99,21 @@ model contactInfo {
|
||||
|
||||
StorageUnit StorageUnit[]
|
||||
Item Item[]
|
||||
project project[]
|
||||
projectAssignedUsers project[] @relation("projectAssignedUsers")
|
||||
}
|
||||
|
||||
model project {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
description String? @db.VarChar(2048)
|
||||
// People
|
||||
manager contactInfo? @relation(fields: [contactInfoId], references: [id]) // Primary, manager of the project
|
||||
assignedUsers contactInfo[] @relation("projectAssignedUsers") // Secondary, assigned users to the project, stagehands, etc.
|
||||
contactInfoId Int?
|
||||
// When does it start and end
|
||||
startTime DateTime?
|
||||
endTime DateTime?
|
||||
}
|
||||
|
||||
/// TODO: Allow multiple types to be used?
|
||||
|
@ -1,5 +1,6 @@
|
||||
import fs from 'node:fs';
|
||||
import _ from 'lodash';
|
||||
import { randomUUID, randomBytes } from 'crypto';
|
||||
|
||||
export type configObject = Record<any, any>;
|
||||
|
||||
@ -14,6 +15,7 @@ export default class config {
|
||||
#configPath: string;
|
||||
//global = {[key: string] : string}
|
||||
global: configObject;
|
||||
replaceSecrets: boolean;
|
||||
|
||||
/**
|
||||
* Creates an instance of config.
|
||||
@ -22,9 +24,10 @@ export default class config {
|
||||
* @param {string} configPath Path to config file.
|
||||
* @param {object} configPreset Default config object with default values.
|
||||
*/
|
||||
constructor(configPath: string, configPreset: object) {
|
||||
constructor(configPath: string, replaceSecrets: boolean, configPreset: object) {
|
||||
this.#configPath = configPath;
|
||||
this.global = configPreset;
|
||||
this.replaceSecrets = replaceSecrets;
|
||||
|
||||
try {
|
||||
// Read config
|
||||
@ -52,6 +55,12 @@ export default class config {
|
||||
*/
|
||||
save_config() {
|
||||
try {
|
||||
// If enabled replace tokens defines as "gen" with random token
|
||||
if (this.replaceSecrets) {
|
||||
// Replace tokens with value "gen"
|
||||
this.generate_secrets(this.global, 'gen')
|
||||
}
|
||||
|
||||
fs.writeFileSync(this.#configPath, JSON.stringify(this.global, null, 8));
|
||||
} catch (err) {
|
||||
console.error(`Could not write config file at ${this.#configPath} due to: ${err}`);
|
||||
@ -59,31 +68,73 @@ export default class config {
|
||||
}
|
||||
console.log(`Successfully written config file to ${this.#configPath}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces each item matching the value of placeholder with a random UUID.
|
||||
* Thanks to https://stackoverflow.com/questions/8085004/iterate-through-nested-javascript-objects
|
||||
* @param {configObject} obj
|
||||
*/
|
||||
generate_secrets(obj: configObject, placeholder: string) {
|
||||
const stack = [obj];
|
||||
while (stack?.length > 0) {
|
||||
const currentObj = stack.pop();
|
||||
Object.keys(currentObj).forEach((key) => {
|
||||
|
||||
if (currentObj[key] === placeholder) {
|
||||
console.log('Generating secret: ' + key);
|
||||
currentObj[key] = randomBytes(48).toString('base64').replace(/\W/g, '');
|
||||
}
|
||||
|
||||
if (typeof currentObj[key] === 'object' && currentObj[key] !== null) {
|
||||
stack.push(currentObj[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
**** Example ****
|
||||
|
||||
import configHandler from './assets/configHandler.js';
|
||||
import ConfigHandlerNG from './assets/configHandlerNG.js';
|
||||
|
||||
// Create a new config instance.
|
||||
export const config = new ConfigHandler(__path + '/config.json', {
|
||||
db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE',
|
||||
http_listen_address: '127.0.0.1',
|
||||
http_port: 3000,
|
||||
sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
|
||||
debug: false
|
||||
});
|
||||
export const config = new ConfigHandler(__path + '/config.json', true, {
|
||||
test1: 't1',
|
||||
test2: 't2',
|
||||
test3: 'gen',
|
||||
test4: 't4',
|
||||
test5: 'gen',
|
||||
testObj: {
|
||||
local: {
|
||||
active: true,
|
||||
users: {
|
||||
user1: 'gen',
|
||||
user2: 'gen',
|
||||
user3: 'gen',
|
||||
user4: 'gen',
|
||||
|
||||
}
|
||||
},
|
||||
oidc: {
|
||||
active: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Base Config:');
|
||||
console.log(config.global);
|
||||
|
||||
console.log('Add some new key to config and call save_config.');
|
||||
console.log('Add some new key to config and call save_config().');
|
||||
config.global.NewKey = 'ThisIsANewKey!'
|
||||
config.save_config()
|
||||
|
||||
console.log('This will add a new key with value gen, but gen gets replaced with a random UUID when save_config() is called.');
|
||||
config.global.someSecret = 'gen'
|
||||
config.save_config() // global.someSecret is getting replaced with some random UUID since it was set to 'gen'.
|
||||
|
||||
console.log('Complete Config:');
|
||||
console.log(config.global);
|
||||
*/
|
||||
|
@ -66,13 +66,16 @@ function returnAllModelFieldData() {
|
||||
* @param {string} [relation_name='id']
|
||||
* @returns {undefined || object} undefined or prisma connect object
|
||||
*/
|
||||
export function parseIntRelation(data: string, relation_name: string = 'id') {
|
||||
export function parseIntRelation(data: string, relation_name: string = 'id', doNotDisconnect: boolean = false) {
|
||||
// This function is perfect. If data is not a valid number, return `undefined`
|
||||
// If it is a valid number return `{connect: {relation_name: yourNumber}}}`
|
||||
// This can be used by prisma to connect relations
|
||||
|
||||
// If the incoming data is null or empty, return a prisma disconnect object instead of a connect one
|
||||
if (data === null || data === '') {
|
||||
if (data === null || data === '' || data === "undefined") {
|
||||
if (doNotDisconnect) {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(`{
|
||||
"disconnect": true
|
||||
}`);
|
||||
@ -85,6 +88,26 @@ export function parseIntRelation(data: string, relation_name: string = 'id') {
|
||||
}`);
|
||||
}
|
||||
|
||||
export function parseIntOrUndefined(data: string) {
|
||||
|
||||
/**
|
||||
* Function to parse a string into a number or return undefined if it is not a number
|
||||
*
|
||||
* @export
|
||||
* @param {string || any} data
|
||||
* @returns {object}
|
||||
*/
|
||||
export function parseIntOrUndefined(data: any) {
|
||||
return isNaN(parseInt(data)) ? undefined : parseInt(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to create a sortBy compatible object from a string
|
||||
*
|
||||
* @export
|
||||
* @param {string} SortField
|
||||
* @param {string} Order
|
||||
* @returns {object}
|
||||
*/
|
||||
export function parseDynamicSortBy(SortField: string, Order: string){
|
||||
return JSON.parse(`{ "${SortField}": "${Order}" }`);
|
||||
}
|
||||
|
@ -4,9 +4,33 @@
|
||||
<div class="background text-center">
|
||||
<div class="row align-items-start">
|
||||
<div class="col-9"></div>
|
||||
<div class="col-3 sidePanel ps-4 text-black">
|
||||
<div class="col-3 sidePanel ps-4 pe-4 text-black">
|
||||
<h1>Log into AssetFlow</h1>
|
||||
<div class="alert alert-danger" role="alert" id="passwordAlarm">
|
||||
User does not exist or password is incorrect.
|
||||
</div>
|
||||
|
||||
<form action="/auth/login" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="userName" class="form-label">Username</label>
|
||||
<input name="username" type="text" class="form-control" id="userName" aria-describedby="userNameHelp" />
|
||||
<!-- <div id="userNameHelp" class="form-text">We'll never share your email with anyone else.</div> -->
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="userPassword" class="form-label">Password</label>
|
||||
<input name="password" type="password" class="form-control" id="userPassword" />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// if url parameter ?failed is set, show the password alarm
|
||||
if (window.location.search.includes("failed")) {
|
||||
document.getElementById("passwordAlarm").style.display = "block";
|
||||
} else {
|
||||
document.getElementById("passwordAlarm").style.display = "none";
|
||||
}
|
||||
</script>
|
||||
<%~ E.includeFile("../partials/foot.eta.html") %>
|
||||
</div>
|
||||
|
@ -1,6 +1,19 @@
|
||||
<%~ E.includeFile("partials/head.eta.html", {"title": "Dashboard"}) %> <%~ E.includeFile("partials/controls.eta.html", {"active": "Dashboard"}) %>
|
||||
|
||||
<h1>Good evening, ${user}</h1>
|
||||
<h1 onclick="doTheConfetti()" class="user-select-none" id="greeting">Good evening, ${user}</h1>
|
||||
<script>
|
||||
// Handle greeting
|
||||
var today = new Date();
|
||||
var curHr = today.getHours();
|
||||
|
||||
if (curHr < 12) {
|
||||
document.getElementById("greeting").innerHTML = "Good morning";
|
||||
} else if (curHr < 18) {
|
||||
document.getElementById("greeting").innerHTML = "Good afternoon";
|
||||
} else {
|
||||
document.getElementById("greeting").innerHTML = "Good evening";
|
||||
}
|
||||
</script>
|
||||
<div class="container text-center">
|
||||
<div class="row">
|
||||
<div class="card col m-2">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<h1><%= it.name %></h1>
|
||||
<div class="container">
|
||||
<p><strong>Comment:</strong> <%= it.comment %></p>
|
||||
<p><strong>Category:</strong> <%= it.category.name %></p>
|
||||
<p><strong>Category:</strong> <% if (it.category == null) { %> <i>No category assigned</i> <% } else { %> <%= it.category.name %> <% } %></p>
|
||||
<p><strong>Amount:</strong> <%= it.amount %></p>
|
||||
<p><strong>SKU:</strong> <%= it.SKU %></p>
|
||||
<p><strong>Status: </strong><% if(it.status == "normal") { %>
|
||||
|
@ -11,17 +11,17 @@
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="itemModifyModalName" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="itemModifyModalName" name="name" required />
|
||||
<input type="text" class="form-control" id="itemModifyModalName" name="name" maxlength="128" required />
|
||||
<div id="itemModifyModalNameText" class="form-text">This name should be unqiue.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="itemModifyModalComment" class="form-label">Comment</label>
|
||||
<input type="text" class="form-control" id="itemModifyModalComment" name="comment" />
|
||||
<input type="text" class="form-control" id="itemModifyModalComment" maxlength="2048" name="comment" />
|
||||
<div id="itemModifyModalDescText" class="form-text">Optional</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="itemModifyModalStorageLocation" class="form-label">Select a storage location</label>
|
||||
<select class="form-select" id="itemModifyModalStorageLocation" name="storageLocation">
|
||||
<select class="form-select" id="itemModifyModalStorageLocation" name="storageLocationId">
|
||||
<option value=""><i>Do not assign a storage location</i></option>
|
||||
<% it.storeLocs.forEach(function(locs){ %>
|
||||
<option value="<%= locs.id %>"><%= locs.name %></option>
|
||||
@ -36,12 +36,12 @@
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="itemModifyModalSKU" class="form-label">SKU</label>
|
||||
<input type="text" class="form-control" id="itemModifyModalSKU" name="sku" />
|
||||
<input type="text" class="form-control" id="itemModifyModalSKU" maxlength="64" name="sku" />
|
||||
<div id="itemModifyModalSKUText" class="form-text">Optional</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="itemModifyModalManuf" class="form-label">Manufacturer</label>
|
||||
<input type="text" class="form-control" id="itemModifyModalManuf" name="manufacturer" />
|
||||
<input type="text" class="form-control" id="itemModifyModalManuf" maxlength="190" name="manufacturer" />
|
||||
<div id="itemModifyModalSKUText" class="form-text">Optional</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
@ -93,13 +93,14 @@
|
||||
<a href="/settings/category/new" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModifyModal" onclick="primeCreateNew()">Create new item</a>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table align-middle">
|
||||
<table class="table align-middle" id="itemList" data-sortable="true" data-search-highlight="true" data-pagination="true" data-page-size="25" data-remember-order="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">SKU</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">Actions</th>
|
||||
<th scope="col" data-field="SKU" class="sku" data-sortable="true">SKU</th>
|
||||
<th scope="col" data-field="name" data-sortable="true">Name</th>
|
||||
<th scope="col" data-field="comment" data-sortable="true" data-width="80">Comment</th>
|
||||
<th scope="col" data-field="status" data-sortable="true">Status</th>
|
||||
<th scope="col" data-field="actions" data-sortable="false" data-searchable="false" data-width="160">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<% if(it.items.length == 0) { %>
|
||||
@ -109,49 +110,8 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
<% } %>
|
||||
<tbody>
|
||||
<% it.items.forEach(function(user){ %>
|
||||
<tr>
|
||||
<td scope="row" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="ID: <%= user.id %>">
|
||||
<% if (user.SKU == null) { %>
|
||||
<i>No SKU assigned</i>
|
||||
<% } else { %> <%= user.SKU %> <% } %></td>
|
||||
<td><%= user.name %></td>
|
||||
<% if(user.status == "normal") { %>
|
||||
|
||||
<td><span class="badge text-bg-success"><%= user.status %></span></td>
|
||||
<% } else if(user.status == "stolen") { %>
|
||||
<td><span class="badge text-bg-danger"><%= user.status %></span></td>
|
||||
<% } else if(user.status == "lost") { %>
|
||||
<td><span class="badge text-bg-warning"><%= user.status %></span></td>
|
||||
<% } else if(user.status == "borrowed") { %>
|
||||
<td><span class="badge text-bg-info"><%= user.status %></span></td>
|
||||
<% } %>
|
||||
<td>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModifyModal" onclick="primeEdit(); getDataForEdit('<%= user.id %>')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="preFillDeleteModalNxt('<%= user.id %>','items','Item')" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<% if(it.maxPages > 1) { %>
|
||||
<nav aria-label="Page selector">
|
||||
<ul class="pagination justify-content-center">
|
||||
<li class="page-item <%= it.currentPage-1 < 1 ? 'disabled' : ''%>"><a class="page-link" href="?page=<%= it.currentPage - 1 %>">Previous</a></li>
|
||||
<% for (var i = 1; i <= it.maxPages; i++) { %>
|
||||
<li class="page-item <%= it.currentPage == i ? 'active' : ''%>"><a class="page-link" href="?page=<%= i %>"><%= i %></a></li>
|
||||
<% } %>
|
||||
|
||||
<li class="page-item <%= it.currentPage+1 > it.maxPages ? 'disabled' : ''%>"><a class="page-link" href="?page=<%= it.currentPage + 1 %>">Next</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<% } %>
|
||||
</div>
|
||||
<script src="/js/editItems.js"></script>
|
||||
<script src="/js/itemPageHandler.js"></script>
|
||||
<%~ E.includeFile("partials/controlsFoot.eta.html") %> <%~ E.includeFile("partials/foot.eta.html") %>
|
||||
|
@ -20,12 +20,12 @@
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="editCategoryModalName" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="editCategoryModalName" name="name" required />
|
||||
<input type="text" class="form-control" id="editCategoryModalName" maxlength="128" name="name" required />
|
||||
<div id="editCategoryModalNameText" class="form-text">This name should be unqiue.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editCategoryModalDescription" class="form-label">Description</label>
|
||||
<input type="text" class="form-control" id="editCategoryModalDescription" name="description" />
|
||||
<input type="text" class="form-control" id="editCategoryModalDescription" maxlength="2048" name="description" />
|
||||
<div id="editCategoryModalDescText" class="form-text">Optional</div>
|
||||
</div>
|
||||
<input type="text" id="editCategoryModalId" name="id" hidden />
|
||||
@ -40,31 +40,15 @@
|
||||
</div>
|
||||
|
||||
<!-- Table with all categories -->
|
||||
<table class="table align-middle">
|
||||
<table class="table align-middle" id="itemList" data-sortable="true" data-search-highlight="true" data-pagination="true" data-page-size="25" data-remember-order="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- <th scope="col">#</th> -->
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Description</th>
|
||||
<th scope="col">Action</th>
|
||||
<th scope="col" data-field="name" data-sortable="true" data-width="300">Name</th>
|
||||
<th scope="col" data-field="description" data-sortable="true">Description</th>
|
||||
<th scope="col" data-field="actions" data-sortable="false" data-searchable="false" data-width="160">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% it.items.forEach(function(user){ %>
|
||||
<tr id="listEntry-<%= user.id %>">
|
||||
<td scope="row" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="ID: <%= user.id %>"><%= user.name %></td>
|
||||
<td><%= user.description %></td>
|
||||
<td>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#editCategoryModal" onclick="primeEdit(); getDataForEdit('<%= user.name %>')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="preFillDeleteModalNxt('<%= user.name %>','categories','Category', 'name')" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
<script src="/js/editCategory.js"></script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<%~ E.includeFile("../partials/head.eta.html", {"title": "Settings"}) %> <%~ E.includeFile("../partials/controls.eta.html", {"active": "SETT"}) %>
|
||||
|
||||
<h1>Manage your AssetFlow instance</h1>
|
||||
<div class="alert alert-success" role="alert">A new version is available. <a href="#" class="alert-link">Click here to update</a></div>
|
||||
<div class="alert alert-success" role="alert" id="updateNotifier">A new version is available. <a href="https://git.project-name-here.de/Project-Name-Here/assetflow/releases" class="alert-link">Click here to update</a></div>
|
||||
<div class="container text-center">
|
||||
<div class="row">
|
||||
<a class="card col m-2" href="/manage/categories">
|
||||
@ -30,5 +30,16 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$.getJSON("/api/v1/version", function (data) {
|
||||
if (data.updateAvailable) {
|
||||
$("#updateNotifier").show();
|
||||
// $("#updateNotifier").find(".alert-link").attr("href", data.url);
|
||||
}else {
|
||||
$("#updateNotifier").hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<%~ E.includeFile("../partials/controlsFoot.eta.html") %> <%~ E.includeFile("../partials/foot.eta.html") %>
|
||||
|
@ -13,7 +13,7 @@
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="storageLocationModalName" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="storageLocationModalName" name="name" required />
|
||||
<input type="text" class="form-control" id="storageLocationModalName" name="name" maxlength="128" required />
|
||||
<div id="storageLocationModalNameText" class="form-text">This name should be unqiue.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
@ -150,37 +150,16 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table align-middle">
|
||||
<table class="table align-middle" id="itemList" data-sortable="true" data-search-highlight="true" data-pagination="true" data-page-size="25" data-remember-order="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Storage Unit</th>
|
||||
<th scope="col">Actions</th>
|
||||
<th scope="col" data-field="name" data-sortable="true">Name</th>
|
||||
<th scope="col" data-field="storageUnit" data-sortable="false">Storage Unit</th>
|
||||
<th scope="col" data-field="actions" data-sortable="false" data-searchable="false" data-width="160">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% it.storLocs.forEach(function(locations){ %>
|
||||
<tr id="listEntry-<%= locations.id %>">
|
||||
<td scope="row" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="ID: <%= locations.id %>"><%= locations.name %></td>
|
||||
<td>
|
||||
<% if (locations.storageUnit == null) { %>
|
||||
<i>No storage unit connected</i>
|
||||
<% } else { %> <%= locations.storageUnit.name %> <% } %>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#storageLocationModal" onclick="primeEdit(); getDataForEditLoc('<%= locations.id %>')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
onclick="preFillDeleteModalNxt('<%= locations.id %>','storageLocations','Storage Location')"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -195,29 +174,15 @@
|
||||
<a class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#storageUnitModal" onclick="primeCreateNew()"><i class="bi bi-building-add"></i> Create new unit</a>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table align-middle">
|
||||
<table class="table align-middle" id="itemListUnit" data-sortable="true" data-search-highlight="true" data-pagination="true" data-page-size="25" data-remember-order="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Address</th>
|
||||
<th scope="col">Actions</th>
|
||||
<th scope="col" data-field="name" data-sortable="true">Name</th>
|
||||
<th scope="col "data-field="address" data-sortable="false">Address</th>
|
||||
<th scope="col" data-field="actions" data-searchable="false" data-width="160">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% it.storUnits.forEach(function(units){ %>
|
||||
<tr id="listEntry-<%= units.id %>">
|
||||
<td scope="row" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="ID: <%= units.id %>"><%= units.name %></td>
|
||||
<td><%= units.contactInfo.street %> <%= units.contactInfo.houseNumber %>, <%= units.contactInfo.city %> <%= units.contactInfo.country %></td>
|
||||
<td>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#storageUnitModal" onclick="primeEdit(); getDataForEdit('<%= units.id %>')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="preFillDeleteModalNxt('<%= units.id %>', 'storageUnits', 'Storage Unit')" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary sticky-top navShadow" style="z-index: 999">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand user-select-none ms-2" onclick="doTheConfetti()" style="cursor: default">
|
||||
<a class="navbar-brand user-select-none ms-2" style="cursor: default" href="/">
|
||||
<img alt="AssetFlow Logo" draggable="false" class="me-2 headLogo" src="/logo/Design_icon.svg"/> AssetFlow</a>
|
||||
<button
|
||||
class="navbar-toggler position-absolute d-md-none collapsed"
|
||||
@ -81,16 +81,38 @@
|
||||
</li> -->
|
||||
</ul>
|
||||
|
||||
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||
<a href="/projects/" class="nav-link"
|
||||
>Projects<span class="badge rounded-pill bg-primary" >
|
||||
Alpha
|
||||
<span class="visually-hidden">Alpha feature</span>
|
||||
</span>
|
||||
</a>
|
||||
</h6>
|
||||
<ul class="nav flex-column mb-2">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= it.active == 'PROJ_HOME' ? 'active' : ''%>" href="/projects/"><i class="bi bi-kanban"></i> Manage Projects </a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= it.active == 'PROJ_LIST' ? 'active' : ''%>" href="/projects/lists"><i class="bi bi-card-checklist"></i> Packaging Lists </a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= it.active == 'PROJ_PEPS' ? 'active' : ''%>" href="/projects/people"><i class="bi bi-people-fill"></i> People </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||
<a href="/manage/" class="nav-link"
|
||||
>Settings
|
||||
<span class="badge rounded-pill bg-danger invisible">
|
||||
2
|
||||
<span class="badge rounded-pill bg-danger invisible" id="notifcationInfo">
|
||||
1
|
||||
<span class="visually-hidden">changes or updates</span>
|
||||
</span>
|
||||
</a>
|
||||
</h6>
|
||||
|
||||
|
||||
<ul class="nav flex-column mb-2">
|
||||
<a class="nav-link <%= it.active == 'SETT_STORE' ? 'active' : ''%>" href="/manage/storages"
|
||||
><i class="bi bi-box-seam"></i> Manage storages
|
||||
@ -137,7 +159,7 @@
|
||||
</div>
|
||||
<!-- Align the mode picker at the bottom of the navbar -->
|
||||
|
||||
<ul class="nav flex-column mb-2 position-absolute bottom-0 align-items-center w-100">
|
||||
<ul class="nav flex-column mb-5 position-absolute bottom-0 align-items-center w-100">
|
||||
<div class="input-group mb-3 justify-content-center w-100">
|
||||
<label class="btn btn-secondary" for="mode_light"><i class="bi bi-brightness-high"></i></label>
|
||||
<input type="radio" class="btn-check" name="options" id="mode_light" autocomplete="off" />
|
||||
@ -177,6 +199,28 @@
|
||||
});
|
||||
</script>
|
||||
</ul>
|
||||
<div onclick="toggleAutoReload();" class="text-secondary versionInfo nav flex-column position-absolute bottom-0 align-items-center w-100" id="versionInfo">AssetFlow Alpha <i>No version info</i> </div>
|
||||
<script>
|
||||
// Request /api/v1/version
|
||||
// If the response is 200, set the commit hash
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/v1/version",
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
$('#versionInfo').text(`AssetFlow Alpha ${data.version} ${data.commit}`);
|
||||
if(data.updateAvailable ){
|
||||
$('#notifcationInfo').show();
|
||||
} else {
|
||||
$('#notifcationInfo').hide();
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
createNewToast('<i class="bi bi-exclamation-triangle-fill"></i> Unable to load version information', "text-bg-danger", 3000, false)
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</nav>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4" style="min-height: 100%">
|
||||
|
@ -6,6 +6,17 @@
|
||||
<script src="/js/handleSidebarTriangles.js"></script>
|
||||
<script src="/js/formHandler.js"></script>
|
||||
<script>
|
||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
|
||||
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
|
||||
function activateTooltips(){
|
||||
// Enable all bootstrap tooltips.
|
||||
// https://getbootstrap.com/docs/5.3/components/tooltips/#enable-tooltips
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||
}
|
||||
function activatePopovers(){
|
||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
|
||||
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
|
||||
}
|
||||
|
||||
activatePopovers();
|
||||
activateTooltips();
|
||||
</script>
|
@ -1,8 +1,2 @@
|
||||
<script>
|
||||
// Enable all bootstrap tooltips.
|
||||
// https://getbootstrap.com/docs/5.3/components/tooltips/#enable-tooltips
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -8,7 +8,6 @@
|
||||
<title>AssetFlow - <%= it.title %></title>
|
||||
<meta name="author" content="[Project-Name-Here]" />
|
||||
|
||||
<!--<link rel="icon" href="/favicon.ico" />-->
|
||||
<link rel="icon" href="/logo/Design_icon.svg" type="image/svg+xml" />
|
||||
|
||||
<script src="/js/handleColorMode.js"></script>
|
||||
@ -21,7 +20,8 @@
|
||||
<script src="/static/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/@popperjs/core/dist/umd/popper.min.js"></script>
|
||||
<script src="/static/tsparticles-confetti/tsparticles.confetti.bundle.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/static/bootstrap-table/dist/bootstrap-table.min.css">
|
||||
<script src="/static/bootstrap-table/dist/bootstrap-table.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- The body and html tag need to be left open! -->
|
||||
|
49
src/frontend/projects/dashboard.eta.html
Normal file
49
src/frontend/projects/dashboard.eta.html
Normal file
@ -0,0 +1,49 @@
|
||||
<%~ E.includeFile("../partials/head.eta.html", {"title": "Projects"}) %> <%~ E.includeFile("../partials/controls.eta.html", {"active": "PROJ_HOME"}) %>
|
||||
|
||||
<h1>Projectmanager</h1>
|
||||
<div class="container text-center">
|
||||
<div class="row">
|
||||
<a class="card col m-2" href="/manage/categories">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title"><i class="bi bi-tag"></i></h1>
|
||||
<p class="card-text">Manage categories</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card col m-2" href="/manage/storages">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title"><i class="bi bi-box-seam"></i></h1>
|
||||
<p class="card-text">Manage storages</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card col m-2" href="/manage/import/csv">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title"><i class="bi bi-filetype-csv"></i></h1>
|
||||
<p class="card-text">Import data via CSV</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card col m-2" href="/manage/import/json">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title"><i class="bi bi-filetype-json"></i></h1>
|
||||
<p class="card-text">Import data via JSON</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Recent projects</h2>
|
||||
<div class="container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Status</th>
|
||||
<!--<th scope="col">Actions</th>-->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<%~ E.includeFile("../partials/controlsFoot.eta.html") %> <%~ E.includeFile("../partials/foot.eta.html") %>
|
55
src/index.ts
55
src/index.ts
@ -5,6 +5,9 @@ import fileUpload from 'express-fileupload';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import * as eta from 'eta';
|
||||
import bodyParser from 'body-parser';
|
||||
import session from 'express-session';
|
||||
import passport from 'passport';
|
||||
import _ from 'lodash';
|
||||
|
||||
// Sentry
|
||||
import * as Sentry from '@sentry/node';
|
||||
@ -28,18 +31,40 @@ export const log = {
|
||||
core: coreLogger,
|
||||
db: coreLogger.scope('DB'),
|
||||
web: coreLogger.scope('WEB'),
|
||||
auth: coreLogger.scope('AUTH'),
|
||||
helper: coreLogger.scope('HELPER')
|
||||
};
|
||||
|
||||
// Create a new config instance.
|
||||
export const config = new ConfigHandler(__path + '/config.json', {
|
||||
export const config = new ConfigHandler(__path + '/config.json', true, {
|
||||
db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE',
|
||||
http_listen_address: '127.0.0.1',
|
||||
http_port: 3000,
|
||||
sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
|
||||
debug: false
|
||||
debug: false,
|
||||
auth: {
|
||||
cookie_secret: 'gen',
|
||||
cookie_secure: true,
|
||||
local: {
|
||||
active: true,
|
||||
users: {}
|
||||
},
|
||||
oidc: {
|
||||
active: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If no local User exists, create the default with a generated password
|
||||
if (_.isEqual(config.global.auth.local.users, {})) {
|
||||
config.global.auth.local.users = {
|
||||
'flowAdmin': 'gen',
|
||||
};
|
||||
config.save_config();
|
||||
}
|
||||
|
||||
|
||||
// TODO: Add errorhandling with some sort of message.
|
||||
export const prisma = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
@ -67,6 +92,17 @@ Sentry.init({
|
||||
environment: config.global.debug ? 'development' : 'production'
|
||||
});
|
||||
|
||||
// TODO: Version check need to be rewritten.
|
||||
app.locals.versionRevLong = require('child_process').execSync('git rev-parse HEAD').toString().trim();
|
||||
app.locals.versionRev = require('child_process').execSync('git rev-parse --short HEAD').toString().trim();
|
||||
app.locals.versionRevLatest = require('child_process').execSync('git ls-remote --refs -q').toString().trim().split('\t')[0];
|
||||
|
||||
if (app.locals.versionRevLong === app.locals.versionRevLatest) {
|
||||
log.core.info(`Running Latest Version (${app.locals.versionRevLong})`);
|
||||
} else {
|
||||
log.core.info(`Running Version: ${app.locals.versionRevLong} (Latest: ${app.locals.versionRevLatest})`);
|
||||
}
|
||||
|
||||
// RequestHandler creates a separate execution context using domains, so that every
|
||||
// transaction/span/breadcrumb is attached to its own Hub instance
|
||||
app.use(Sentry.Handlers.requestHandler());
|
||||
@ -82,14 +118,23 @@ app.use(bodyParser.urlencoded({ extended: false }));
|
||||
// Using bodyParser to parse JSON bodies into JS objects
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// Session store
|
||||
// TODO: Move secret to config -> Autogenerate.
|
||||
app.use(
|
||||
session({
|
||||
secret: config.global.auth.cookie_secret,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: { secure: config.global.auth.cookie_secure }
|
||||
})
|
||||
);
|
||||
app.use(passport.authenticate('session'));
|
||||
|
||||
app.use(fileUpload());
|
||||
app.use(express.static(__path + '/static'));
|
||||
|
||||
app.use(routes);
|
||||
|
||||
// The error handler must be before any other error middleware and after all controllers
|
||||
app.use(Sentry.Handlers.errorHandler());
|
||||
|
||||
app.listen(config.global.http_port, config.global.http_listen_address, () => {
|
||||
log.web.info(`Listening at http://${config.global.http_listen_address}:${config.global.http_port}`);
|
||||
});
|
||||
|
21
src/middleware/auth.mw.ts
Normal file
21
src/middleware/auth.mw.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export function checkAuthentication(req: any, res: any, next: Function) {
|
||||
if (req.isAuthenticated()) {
|
||||
//req.isAuthenticated() will return true if user is logged in
|
||||
next();
|
||||
} else {
|
||||
res.redirect('/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
// const checkIsInRole = (...roles) => (req, res, next) => {
|
||||
// if (!req.user) {
|
||||
// return res.redirect('/login')
|
||||
// }
|
||||
|
||||
// const hasRole = roles.find(role => req.user.role === role)
|
||||
// if (!hasRole) {
|
||||
// return res.redirect('/login')
|
||||
// }
|
||||
|
||||
// return next()
|
||||
// }
|
@ -1,14 +1,21 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { prisma, __path, log } from '../../../index.js';
|
||||
import { parseIntOrUndefined, parseDynamicSortBy } from '../../../assets/helper.js';
|
||||
|
||||
// Get category.
|
||||
function get(req: Request, res: Response) {
|
||||
// Get category
|
||||
async function get(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.query.name) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
return;
|
||||
if (req.query.sort === undefined) {
|
||||
req.query.sort = 'id';
|
||||
}
|
||||
if (req.query.order === undefined) {
|
||||
req.query.order = 'asc';
|
||||
}
|
||||
if (req.query.search === undefined) {
|
||||
req.query.search = '';
|
||||
}
|
||||
|
||||
if (req.query.name) {
|
||||
prisma.itemCategory
|
||||
.findUnique({
|
||||
where: {
|
||||
@ -17,22 +24,81 @@ function get(req: Request, res: Response) {
|
||||
})
|
||||
.then((item) => {
|
||||
if (item) {
|
||||
res.status(200).json(JSON.stringify(item));
|
||||
res.status(200).json(item);
|
||||
} else {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'Category does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Category does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
} else {
|
||||
// Get all items
|
||||
const itemCountNotFiltered = await prisma.itemCategory.count({});
|
||||
|
||||
// Get all items (filtered)
|
||||
const itemCountFiltered = await prisma.itemCategory.count({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
description: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString())
|
||||
});
|
||||
|
||||
prisma.itemCategory
|
||||
.findMany({
|
||||
take: parseIntOrUndefined(req.query.limit),
|
||||
skip: parseIntOrUndefined(req.query.offset),
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString()),
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
description: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json({ total: itemCountFiltered, totalNotFiltered: itemCountNotFiltered, items: items });
|
||||
} else {
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Item does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create category.
|
||||
function post(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.name) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -48,18 +114,21 @@ function post(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'created', id: data.id });
|
||||
|
||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created category', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'Category already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'Category already exists' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -68,7 +137,7 @@ function post(req: Request, res: Response) {
|
||||
async function patch(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id || !req.body.name) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -81,12 +150,12 @@ async function patch(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'Category does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Category does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.itemCategory
|
||||
@ -97,20 +166,27 @@ async function patch(req: Request, res: Response) {
|
||||
data: {
|
||||
name: req.body.name,
|
||||
description: req.body.description
|
||||
},
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(201).json({ status: 'updated' });
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'UPDATED', message: 'Successfully updated category', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'Category already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'Category already exists' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -119,7 +195,7 @@ async function patch(req: Request, res: Response) {
|
||||
async function del(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -132,12 +208,12 @@ async function del(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'Category does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Category does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.itemCategory
|
||||
@ -147,11 +223,11 @@ async function del(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(200).json({ errorcode: 'DELETED', error: 'Sucessfully deleted entry' });
|
||||
res.status(200).json({ status: 'DELETED', message: 'Successfully deleted category' });
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
|
||||
|
244
src/routes/api/v1/contactInfo.ts
Normal file
244
src/routes/api/v1/contactInfo.ts
Normal file
@ -0,0 +1,244 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { prisma, __path, log } from '../../../index.js';
|
||||
import { parseIntOrUndefined, parseDynamicSortBy } from '../../../assets/helper.js';
|
||||
|
||||
// Get category
|
||||
async function get(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (req.query.sort === undefined) {
|
||||
req.query.sort = 'id';
|
||||
}
|
||||
if (req.query.order === undefined) {
|
||||
req.query.order = 'asc';
|
||||
}
|
||||
if (req.query.search === undefined) {
|
||||
req.query.search = '';
|
||||
}
|
||||
|
||||
if (req.query.id) {
|
||||
prisma.contactInfo
|
||||
.findUnique({
|
||||
where: {
|
||||
id: parseInt(req.query.id.toString())
|
||||
}
|
||||
})
|
||||
.then((item) => {
|
||||
if (item) {
|
||||
res.status(200).json(item);
|
||||
} else {
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'ContactInfo does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
} else {
|
||||
// Get all items
|
||||
const itemCountNotFiltered = await prisma.contactInfo.count({});
|
||||
|
||||
// Get all items (filtered)
|
||||
const itemCountFiltered = await prisma.contactInfo.count({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
street: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString())
|
||||
});
|
||||
|
||||
prisma.contactInfo
|
||||
.findMany({
|
||||
take: parseIntOrUndefined(req.query.limit),
|
||||
skip: parseIntOrUndefined(req.query.offset),
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString()),
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
street: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json({ total: itemCountFiltered, totalNotFiltered: itemCountNotFiltered, items: items });
|
||||
} else {
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'ContactInfo does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create category.
|
||||
function post(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.street || !req.body.houseNumber || !req.body.zipCode || !req.body.city || !req.body.country) {
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Save data.
|
||||
prisma.contactInfo
|
||||
.create({
|
||||
data: {
|
||||
name: req.body.name,
|
||||
lastName: req.body.lastName,
|
||||
street: req.body.street,
|
||||
houseNumber: req.body.houseNumber,
|
||||
zipCode: req.body.zipCode,
|
||||
city: req.body.city,
|
||||
country: req.body.country,
|
||||
},
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created contactInfo', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'ContactInfo already exists' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update category.
|
||||
async function patch(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id || !req.body.name) {
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the category id exists. If not return 410 Gone.
|
||||
try {
|
||||
const result = await prisma.contactInfo.findUnique({
|
||||
where: {
|
||||
id: parseInt(req.body.id)
|
||||
}
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Category does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.contactInfo
|
||||
.update({
|
||||
where: {
|
||||
id: parseInt(req.body.id)
|
||||
},
|
||||
data: {
|
||||
name: req.body.name,
|
||||
lastName: req.body.lastName,
|
||||
street: req.body.street,
|
||||
houseNumber: req.body.houseNumber,
|
||||
zipCode: req.body.zipCode,
|
||||
city: req.body.city,
|
||||
country: req.body.country,
|
||||
},
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'UPDATED', message: 'Successfully updated category', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'Category already exists' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Delete category
|
||||
async function del(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id) {
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Does the id exist? If not return 410 Gone.
|
||||
try {
|
||||
const result = await prisma.contactInfo.findUnique({
|
||||
where: {
|
||||
id: parseInt(req.body.id)
|
||||
}
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'ContactInfo does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.contactInfo
|
||||
.delete({
|
||||
where: {
|
||||
id: parseInt(req.body.id)
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(200).json({ status: 'DELETED', message: 'Successfully deleted contactInfo' });
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
|
||||
export default { get, post, patch, del };
|
@ -1,4 +1,5 @@
|
||||
import express from 'express';
|
||||
import passport from 'passport';
|
||||
|
||||
// Route imports
|
||||
import testRoute from './test.js';
|
||||
@ -6,6 +7,8 @@ import itemRoute from './items.js';
|
||||
import categoryRoute from './categories.js';
|
||||
import storageUnitRoute from './storageUnits.js';
|
||||
import storageLocationRoute from './storageLocations.js';
|
||||
import contactInfo from './contactInfo.js';
|
||||
import versionRoute from './version.js'
|
||||
|
||||
import search_routes from './search/index.js';
|
||||
|
||||
@ -27,7 +30,9 @@ Router.route('/categories').get(categoryRoute.get).post(categoryRoute.post).patc
|
||||
// TODO: Migrate routes to lowercase.
|
||||
Router.route('/storageUnits').get(storageUnitRoute.get).post(storageUnitRoute.post).patch(storageUnitRoute.patch).delete(storageUnitRoute.del);
|
||||
Router.route('/storageLocations').get(storageLocationRoute.get).post(storageLocationRoute.post).patch(storageLocationRoute.patch).delete(storageLocationRoute.del);
|
||||
Router.route('/contactInfo').get(contactInfo.get).post(contactInfo.post).patch(contactInfo.patch).delete(contactInfo.del);
|
||||
|
||||
Router.route('/version').get(versionRoute.get);
|
||||
Router.use('/search', search_routes);
|
||||
|
||||
Router.route('/test').get(testRoute.get);
|
||||
|
@ -1,19 +1,24 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { prisma, __path, log } from '../../../index.js';
|
||||
import { itemStatus } from '@prisma/client';
|
||||
import { parseIntRelation, parseIntOrUndefined } from '../../../assets/helper.js';
|
||||
import { parseIntRelation, parseIntOrUndefined, parseDynamicSortBy } from '../../../assets/helper.js';
|
||||
// Get item.
|
||||
function get(req: Request, res: Response) {
|
||||
if (req.query.getAll === undefined) {
|
||||
// Check if required fields are present
|
||||
if (!req.query.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
return;
|
||||
async function get(req: Request, res: Response) {
|
||||
// Set sane defaults if undefined.
|
||||
if (req.query.sort === undefined) {
|
||||
req.query.sort = 'id';
|
||||
}
|
||||
if (req.query.order === undefined) {
|
||||
req.query.order = 'asc';
|
||||
}
|
||||
if (req.query.search === undefined) {
|
||||
req.query.search = '';
|
||||
}
|
||||
|
||||
if (req.query.id) {
|
||||
// Check if number is a valid integer
|
||||
if (!Number.isInteger(parseInt(req.query.id.toString()))) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'The id field must be an integer' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
prisma.item
|
||||
@ -38,18 +43,63 @@ function get(req: Request, res: Response) {
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json(JSON.stringify(items));
|
||||
res.status(200).json(items);
|
||||
} else {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'Item does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Item does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
} else {
|
||||
// Get all items
|
||||
const itemCountNotFiltered = await prisma.item.count({});
|
||||
|
||||
// Get all items (filtered)
|
||||
const itemCountFiltered = await prisma.item.count({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
SKU: {
|
||||
// Probably use prisma's Full-text search if it's out of beta
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString())
|
||||
});
|
||||
// log.core.debug('Dynamic relation:', parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString()));
|
||||
|
||||
prisma.item
|
||||
.findMany({
|
||||
take: parseIntOrUndefined(req.query.limit),
|
||||
skip: parseIntOrUndefined(req.query.offset),
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
SKU: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString()),
|
||||
// Get contactInfo, category, storageLocation( storageUnit<contactInfo> ) from relations.
|
||||
include: {
|
||||
contactInfo: true,
|
||||
@ -67,14 +117,14 @@ function get(req: Request, res: Response) {
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json(JSON.stringify(items));
|
||||
res.status(200).json({ total: itemCountFiltered, totalNotFiltered: itemCountNotFiltered, items: items });
|
||||
} else {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'Item does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Item does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -83,13 +133,13 @@ function get(req: Request, res: Response) {
|
||||
function post(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.name) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if status is valid.
|
||||
if (req.body.status !== undefined && !Object.keys(itemStatus).includes(req.body.status)) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: `Status is not valid, valid values are: ${Object.keys(itemStatus).join(', ')}` });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: `Status is not valid, valid values are: ${Object.keys(itemStatus).join(', ')}` });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -97,14 +147,14 @@ function post(req: Request, res: Response) {
|
||||
.create({
|
||||
data: {
|
||||
SKU: req.body.sku,
|
||||
amount: parseIntOrUndefined(req.body.ammount), // FIXME: This is silently failing if NaN..
|
||||
amount: parseIntOrUndefined(req.body.amount), // FIXME: This is silently failing if NaN..
|
||||
name: req.body.name,
|
||||
comment: req.body.comment,
|
||||
status: req.body.status, // Only enum(itemStatus) values are valid
|
||||
// Relations
|
||||
contactInfo: parseIntRelation(req.body.contactInfoId),
|
||||
category: parseIntRelation(req.body.categoryId),
|
||||
storageLocation: parseIntRelation(req.body.storageLocationId),
|
||||
contactInfo: parseIntRelation(req.body.contactInfoId, undefined, true),
|
||||
category: parseIntRelation(req.body.categoryId, undefined, true),
|
||||
storageLocation: parseIntRelation(req.body.storageLocationId, undefined, true),
|
||||
|
||||
manufacturer: req.body.manufacturer,
|
||||
|
||||
@ -120,22 +170,26 @@ function post(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'created', id: data.id });
|
||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created item', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'Item already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'Item already exists' });
|
||||
} else if (err.code == 'P2003') {
|
||||
// P2003 -> "Foreign key constraint failed on the field: {field_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
// FIXME: Is this errormessage right?
|
||||
res.status(404).json({ errorcode: 'NOT_EXISTING', error: 'Specified item does not exist' });
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Item does not exist' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -144,19 +198,19 @@ function post(req: Request, res: Response) {
|
||||
async function patch(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if number is a valid integer
|
||||
if (!Number.isInteger(parseInt(req.body.id.toString()))) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'The id field must be an integer' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'id field must be an integer' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if status is valid.
|
||||
if (req.body.status !== undefined && !Object.keys(itemStatus).includes(req.body.status)) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: `Status is not valid, valid values are: ${Object.keys(itemStatus).join(', ')}` });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: `Status is not valid, valid values are: ${Object.keys(itemStatus).join(', ')}` });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -167,7 +221,7 @@ async function patch(req: Request, res: Response) {
|
||||
},
|
||||
data: {
|
||||
SKU: req.body.sku,
|
||||
amount: parseIntOrUndefined(req.body.ammount), // FIXME: This is silently failing if NaN..
|
||||
amount: parseIntOrUndefined(req.body.amount), // FIXME: This is silently failing if NaN..
|
||||
name: req.body.name,
|
||||
comment: req.body.comment,
|
||||
status: req.body.status, // Only enum(itemStatus) values are valid
|
||||
@ -187,24 +241,31 @@ async function patch(req: Request, res: Response) {
|
||||
// }
|
||||
//},
|
||||
createdBy: req.body.createdBy
|
||||
},
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(201).json({ status: 'updated' });
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'UPDATED', message: 'Successfully updated item', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'Item already exists', err: err });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'Item already exists' });
|
||||
} else if (err.code == 'P2003') {
|
||||
// P2003 -> "Foreign key constraint failed on the field: {field_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ errorcode: 'NOT_EXISTING', error: 'Specified item does not exist' });
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Item does not exist' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -213,7 +274,7 @@ async function patch(req: Request, res: Response) {
|
||||
async function del(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -226,12 +287,12 @@ async function del(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'Item does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'Item does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.item
|
||||
@ -241,11 +302,11 @@ async function del(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(200).json({ errorcode: 'DELETED', error: 'Sucessfully deleted entry' });
|
||||
res.status(200).json({ status: 'DELETED', message: 'Successfully deleted item' });
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ function get(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then((items) => {
|
||||
res.status(200).json(JSON.stringify(items));
|
||||
res.status(200).json(items);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
|
@ -1,14 +1,20 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { prisma, __path, log } from '../../../index.js';
|
||||
import { parseIntOrUndefined, parseDynamicSortBy, parseIntRelation } from '../../../assets/helper.js';
|
||||
|
||||
// Get storageLocation.
|
||||
function get(req: Request, res: Response) {
|
||||
if (req.query.getAll === undefined) {
|
||||
// Check if required fields are present.
|
||||
if (!req.query.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
return;
|
||||
async function get(req: Request, res: Response) {
|
||||
if (req.query.sort === undefined) {
|
||||
req.query.sort = 'id';
|
||||
}
|
||||
if (req.query.order === undefined) {
|
||||
req.query.order = 'asc';
|
||||
}
|
||||
if (req.query.search === undefined) {
|
||||
req.query.search = '';
|
||||
}
|
||||
|
||||
if (req.query.id) {
|
||||
prisma.storageLocation
|
||||
.findUnique({
|
||||
where: {
|
||||
@ -21,33 +27,56 @@ function get(req: Request, res: Response) {
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json(JSON.stringify(items));
|
||||
res.status(200).json(items);
|
||||
} else {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'storageLocation does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageLocation does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
} else {
|
||||
// Get all items
|
||||
const itemCountNotFiltered = await prisma.storageLocation.count({});
|
||||
|
||||
// Get all items (filtered)
|
||||
const itemCountFiltered = await prisma.storageLocation.count({
|
||||
where: {
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString())
|
||||
});
|
||||
|
||||
prisma.storageLocation
|
||||
.findMany({
|
||||
take: parseIntOrUndefined(req.query.limit),
|
||||
skip: parseIntOrUndefined(req.query.offset),
|
||||
where: {
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
// Get storageUnit from relation.
|
||||
include: {
|
||||
storageUnit: true
|
||||
}
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString()),
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json(JSON.stringify(items));
|
||||
res.status(200).json({ total: itemCountFiltered, totalNotFiltered: itemCountNotFiltered, items: items });
|
||||
} else {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'storageLocation does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageLocation does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -56,7 +85,7 @@ function get(req: Request, res: Response) {
|
||||
function post(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.name) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
// Create storageLocation with existing storageUnit.
|
||||
@ -71,22 +100,26 @@ function post(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'created', id: data.id });
|
||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created storageLocation', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'storageLocation already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'storageLocation already exists' });
|
||||
} else if (err.code == 'P2003') {
|
||||
// P2003 -> "Foreign key constraint failed on the field: {field_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
// FIXME: Is this errormessage right?
|
||||
res.status(404).json({ error: 'specified storageUnitId does not exist' });
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageUnitId does not exist' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -95,7 +128,7 @@ function post(req: Request, res: Response) {
|
||||
async function patch(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id || !req.body.name || !req.body.storageUnitId) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -108,12 +141,12 @@ async function patch(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(404).json({ error: 'storageLocation does not exist.' });
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageLocation does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.storageLocation
|
||||
@ -123,26 +156,33 @@ async function patch(req: Request, res: Response) {
|
||||
},
|
||||
data: {
|
||||
name: req.body.name,
|
||||
storageUnitId: parseInt(req.body.storageUnitId) || undefined
|
||||
storageUnit: parseIntRelation(req.body.storageUnitId)
|
||||
},
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(201).json({ status: 'updated' });
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'UPDATED', message: 'Successfully updated storageLocation', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'storageLocation already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'storageLocation already exists' });
|
||||
} else if (err.code == 'P2003') {
|
||||
// P2003 -> "Foreign key constraint failed on the field: {field_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
// FIXME: Is this errormessage right?
|
||||
res.status(404).json({ error: 'specified storageUnitId does not exist' });
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageUnitId does not exist' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -151,7 +191,7 @@ async function patch(req: Request, res: Response) {
|
||||
async function del(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -164,12 +204,12 @@ async function del(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'storageLocation does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageLocation does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.storageLocation
|
||||
@ -179,11 +219,11 @@ async function del(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(200).json({ errorcode: 'DELETED', error: 'Sucessfully deleted entry' });
|
||||
res.status(200).json({ status: 'DELETED', message: 'Successfully deleted storageLocation' });
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,20 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { prisma, __path, log } from '../../../index.js';
|
||||
import { contactType } from '@prisma/client';
|
||||
import { parseDynamicSortBy, parseIntOrUndefined } from '../../../assets/helper.js';
|
||||
|
||||
// Get storageUnit.
|
||||
function get(req: Request, res: Response) {
|
||||
if (req.query.getAll === undefined) {
|
||||
// Check if required fields are present.
|
||||
if (!req.query.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
return;
|
||||
async function get(req: Request, res: Response) {
|
||||
if (req.query.sort === undefined) {
|
||||
req.query.sort = 'id';
|
||||
}
|
||||
if (req.query.order === undefined) {
|
||||
req.query.order = 'asc';
|
||||
}
|
||||
if (req.query.search === undefined) {
|
||||
req.query.search = '';
|
||||
}
|
||||
if (req.query.id) {
|
||||
prisma.storageUnit
|
||||
.findUnique({
|
||||
where: {
|
||||
@ -23,34 +28,57 @@ function get(req: Request, res: Response) {
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json(JSON.stringify(items));
|
||||
res.status(200).json(items);
|
||||
} else {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'storageUnit does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageUnit does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
} else {
|
||||
// Get all items
|
||||
const itemCountNotFiltered = await prisma.storageUnit.count({});
|
||||
|
||||
// Get all items (filtered)
|
||||
const itemCountFiltered = await prisma.storageUnit.count({
|
||||
where: {
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString())
|
||||
});
|
||||
|
||||
prisma.storageUnit
|
||||
.findMany({
|
||||
take: parseIntOrUndefined(req.query.limit),
|
||||
skip: parseIntOrUndefined(req.query.offset),
|
||||
// Get contactInfo and StorageLocation from relation.
|
||||
include: {
|
||||
contactInfo: true,
|
||||
StorageLocation: true
|
||||
},
|
||||
where: {
|
||||
name: {
|
||||
// @ts-ignore
|
||||
contains: req.query.search.length > 0 ? req.query.search : ''
|
||||
}
|
||||
},
|
||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString())
|
||||
})
|
||||
.then((items) => {
|
||||
if (items) {
|
||||
res.status(200).json(JSON.stringify(items));
|
||||
res.status(200).json({ total: itemCountFiltered, totalNotFiltered: itemCountNotFiltered, items: items });
|
||||
} else {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'storageUnit does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageUnit does not exist' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -61,7 +89,7 @@ function post(req: Request, res: Response) {
|
||||
if (req.body.locationId === 'META_CREATENEW') {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.street || !req.body.houseNumber || !req.body.zipCode || !req.body.city || !req.body.country || !req.body.name) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -86,23 +114,27 @@ function post(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'created', id: data.id });
|
||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created storageUnit', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'storageUnit already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'storageUnit already exists' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.name || !req.body.locationId) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
// Create storageUnit with existing location.
|
||||
@ -121,17 +153,21 @@ function post(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'created', id: data.id });
|
||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created storageUnit', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'storageUnit already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'storageUnit already exists' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -141,7 +177,7 @@ function post(req: Request, res: Response) {
|
||||
async function patch(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id || !req.body.name || !req.body.locationId) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -154,12 +190,12 @@ async function patch(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'storageUnit does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageUnit does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
// Check if the locationId(contactInfo) exists. If not return 410 Gone.
|
||||
@ -171,12 +207,12 @@ async function patch(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'locationId does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'locationId does not exist' });
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.storageUnit
|
||||
@ -191,20 +227,27 @@ async function patch(req: Request, res: Response) {
|
||||
id: parseInt(req.body.locationId) // TODO: Rename to contactInfoId
|
||||
}
|
||||
}
|
||||
},
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(201).json({ status: 'updated' });
|
||||
.then((data) => {
|
||||
res.status(201).json({ status: 'UPDATED', message: 'Successfully updated storageUnit', id: data.id });
|
||||
})
|
||||
.catch((err) => {
|
||||
// Check if an entry already exists.
|
||||
if (err.code === 'P2002') {
|
||||
// P2002 -> "Unique constraint failed on the {constraint}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(409).json({ errorcode: 'EXISTING', error: 'storageUnit already exists' });
|
||||
res.status(409).json({ status: 'ERROR', errorcode: 'EXISTING', message: 'storageUnit already exists' });
|
||||
} else if (err.code == 'P2000') {
|
||||
// P2000 -> "The provided value for the column is too long for the column's type. Column: {column_name}"
|
||||
// https://www.prisma.io/docs/reference/api-reference/error-reference
|
||||
res.status(404).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more fields exceed the maximum length restriction' });
|
||||
} else {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -213,7 +256,7 @@ async function patch(req: Request, res: Response) {
|
||||
async function del(req: Request, res: Response) {
|
||||
// Check if required fields are present.
|
||||
if (!req.body.id) {
|
||||
res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' });
|
||||
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -226,13 +269,13 @@ async function del(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
if (result === null) {
|
||||
res.status(410).json({ errorcode: 'NOT_EXISTING', error: 'storageUnit does not exist' });
|
||||
res.status(410).json({ status: 'ERROR', errorcode: 'NOT_EXISTING', message: 'storageUnit does not exist' });
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
}
|
||||
|
||||
prisma.storageUnit
|
||||
@ -242,11 +285,11 @@ async function del(req: Request, res: Response) {
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
res.status(200).json({ errorcode: 'DELETED', error: 'Sucessfully deleted entry' });
|
||||
res.status(200).json({ status: 'DELETED', message: 'Successfully deleted storageUnit' });
|
||||
})
|
||||
.catch((err) => {
|
||||
log.db.error(err);
|
||||
res.status(500).json({ errorcode: 'DB_ERROR', error: err });
|
||||
res.status(500).json({ status: 'ERROR', errorcode: 'DB_ERROR', error: err, message: 'An error occurred during the database operation' });
|
||||
});
|
||||
}
|
||||
|
||||
|
13
src/routes/api/v1/version.ts
Normal file
13
src/routes/api/v1/version.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
const revision = req.app.locals.versionRev;
|
||||
let updateAvailable = false;
|
||||
if(req.app.locals.versionRevLong !== req.app.locals.versionRevLatest) {
|
||||
updateAvailable = true;
|
||||
}
|
||||
|
||||
res.status(200).send({ version: '1.0.0', commit: revision, updateAvailable: updateAvailable });
|
||||
};
|
||||
|
||||
export default { get };
|
90
src/routes/auth/index.ts
Normal file
90
src/routes/auth/index.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import passport from 'passport';
|
||||
import { Strategy as LocalStrategy } from 'passport-local';
|
||||
import express, { Request, Response } from 'express';
|
||||
import { prisma, __path, log, config, app } from '../../index.js';
|
||||
|
||||
// Middleware Imports
|
||||
import { checkAuthentication } from '../../middleware/auth.mw.js'
|
||||
|
||||
/* Configure password authentication strategy.
|
||||
*
|
||||
* The `LocalStrategy` authenticates users by verifying a username and password.
|
||||
* The strategy parses the username and password from the request and calls the
|
||||
* `verify` function.
|
||||
*
|
||||
* The `verify` function queries the database for the user record and verifies
|
||||
* the password by hashing the password supplied by the user and comparing it to
|
||||
* the hashed password stored in the database. If the comparison succeeds, the
|
||||
* user is authenticated; otherwise, not.
|
||||
*/
|
||||
passport.use(
|
||||
new LocalStrategy(function verify(username, password, cb) {
|
||||
//log.auth.debug('LocalStrategy:', username, password);
|
||||
|
||||
for (const [user, pass] of Object.entries(config.global.auth.local.users)) {
|
||||
//log.auth.debug('Loop(REQ):', username, password);
|
||||
//log.auth.debug('Loop(CFG):', user, pass);
|
||||
|
||||
if (user.toLowerCase() === username.toLowerCase() && pass === password) {
|
||||
log.auth.debug('LocalStrategy: success');
|
||||
return cb(null, { username: username }); // This is the user object.
|
||||
}
|
||||
}
|
||||
log.auth.debug('LocalStrategy: failed');
|
||||
return cb(null, false, { message: 'Incorrect username or password.' });
|
||||
})
|
||||
/*
|
||||
1. If the user not found in DB,
|
||||
done (null, false)
|
||||
|
||||
2. If the user found in DB, but password does not match,
|
||||
done (null, false)
|
||||
|
||||
3. If user found in DB and password match,
|
||||
done (null, {authenticated_user})
|
||||
*/
|
||||
);
|
||||
|
||||
/* Configure session management.
|
||||
*
|
||||
* When a login session is established, information about the user will be
|
||||
* stored in the session. This information is supplied by the `serializeUser`
|
||||
* function, which is yielding the user ID and username.
|
||||
*
|
||||
* As the user interacts with the app, subsequent requests will be authenticated
|
||||
* by verifying the session. The same user information that was serialized at
|
||||
* session establishment will be restored when the session is authenticated by
|
||||
* the `deserializeUser` function.
|
||||
*
|
||||
*/
|
||||
passport.serializeUser(function (user: any, cb) {
|
||||
process.nextTick(function () {
|
||||
// log.auth.debug('Called seriealizeUser');
|
||||
// log.auth.debug('user:', user);
|
||||
return cb(null, {
|
||||
username: user.username
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
passport.deserializeUser(function (user, cb) {
|
||||
process.nextTick(function () {
|
||||
// log.auth.debug('Called deseriealizeUser');
|
||||
return cb(null, user);
|
||||
});
|
||||
});
|
||||
|
||||
// Route imports
|
||||
import testRoute from './test.js';
|
||||
import loginRoute from './login.js';
|
||||
//import logoutRoute from './login.js'
|
||||
|
||||
// Router base is '/auth'
|
||||
const Router = express.Router({ strict: false });
|
||||
|
||||
Router.route('/login').get(loginRoute.get);
|
||||
Router.route('/login').post(passport.authenticate('local', { successRedirect: '/', failureRedirect: '/auth/login?failed' }));
|
||||
|
||||
Router.route('/test').get(checkAuthentication, testRoute.get);
|
||||
|
||||
export default Router;
|
@ -1,5 +1,7 @@
|
||||
import passport from 'passport';
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import { prisma, __path } from '../../../index.js';
|
||||
import { prisma, __path, log } from '../../index.js';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
res.render(__path + '/src/frontend/auth/login.eta.html'); //, { items: items });
|
7
src/routes/auth/test.ts
Normal file
7
src/routes/auth/test.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
res.status(200).send('Auth Test Successful!');
|
||||
};
|
||||
|
||||
export default { get };
|
@ -7,6 +7,7 @@ import testRoute from './test.js';
|
||||
import dashboardRoute from './dashboard.js';
|
||||
import itemsRoute from './items.js';
|
||||
import manage_routes from './manage/index.js';
|
||||
import project_routes from './projects/index.js';
|
||||
|
||||
// Router base is '/'
|
||||
const Router = express.Router({ strict: false });
|
||||
@ -14,11 +15,13 @@ const Router = express.Router({ strict: false });
|
||||
Router.route('/test').get(testRoute.get);
|
||||
Router.route('/items').get(itemsRoute.get);
|
||||
|
||||
Router.route('/:id(\\w{8})').get(skuRoute.get);
|
||||
Router.use('/projects', project_routes); // has to be before skuRoute
|
||||
Router.route('/:id(\\w{8})').get(skuRoute.get); // we should probably deprecate this
|
||||
Router.route('/s/:id').get(skuRouteDash.get);
|
||||
|
||||
Router.use('/manage', manage_routes);
|
||||
|
||||
|
||||
Router.route('/').get(dashboardRoute.get);
|
||||
|
||||
export default Router;
|
||||
|
@ -29,7 +29,9 @@ function get(req: Request, res: Response) {
|
||||
res.send(html);
|
||||
});
|
||||
} else {
|
||||
res.send('Item not found');
|
||||
Eta.renderFile(__path + '/src/frontend/errors/404.eta.html', item).then((html) => {
|
||||
res.status(404).send(html);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2,34 +2,13 @@ import { Request, Response } from 'express';
|
||||
import { prisma, __path, log } from '../../index.js';
|
||||
|
||||
async function get(req: Request, res: Response) {
|
||||
// If no page is provided redirect to first
|
||||
if (req.query.page === undefined) {
|
||||
res.redirect('?page=1');
|
||||
return;
|
||||
}
|
||||
|
||||
let page = parseInt(req.query.page.toString());
|
||||
const itemCount = await prisma.item.count({}); // Count all items in the DB
|
||||
|
||||
const takeSize = 25; // Amount of times per page
|
||||
const pageSize = Math.ceil(itemCount / takeSize); // Amount of pages, always round up
|
||||
|
||||
// If page is less then 1 or more then the max page size redirect to first or last page. If itemCount is 0 do not redirect.
|
||||
if (page < 1) {
|
||||
res.redirect('?page=1');
|
||||
return;
|
||||
} else if (page > pageSize && itemCount !== 0) {
|
||||
res.redirect('?page=' + pageSize);
|
||||
return;
|
||||
}
|
||||
prisma.item
|
||||
.findMany({ skip: (page - 1) * takeSize, take: takeSize }) // Skip the amount of items per page times the page number minus 1; skip has to be (page-1)*takeSize because skip is 0 indexed
|
||||
.findMany({}) // Skip the amount of items per page times the page number minus 1; skip has to be (page-1)*takeSize because skip is 0 indexed
|
||||
.then((items) => {
|
||||
prisma.storageLocation.findMany({}).then((locations) => {
|
||||
prisma.itemCategory.findMany({}).then((categories) => {
|
||||
prisma.contactInfo.findMany({}).then((contactInfo) => {
|
||||
|
||||
res.render(__path + '/src/frontend/items.eta.html', { items: items, currentPage: page, maxPages: pageSize, storeLocs: locations, categories: categories, contactInfo: contactInfo });
|
||||
res.render(__path + '/src/frontend/items.eta.html', { items: items, storeLocs: locations, categories: categories, contactInfo: contactInfo });
|
||||
})
|
||||
});
|
||||
});
|
||||
|
@ -7,6 +7,12 @@ function get(req: Request, res: Response) {
|
||||
.findMany({})
|
||||
.then((items) => {
|
||||
// Count amount of total items
|
||||
// Replace "null" with an empty string
|
||||
items.forEach((item) => {
|
||||
if (item.description == null || item.description == "null") {
|
||||
item.description = '';
|
||||
}
|
||||
});
|
||||
res.render(__path + '/src/frontend/manage/categoryManager.eta.html', { items: items });
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -8,7 +8,7 @@ function post(req: Request, res: Response) {
|
||||
// Handle file upload and import
|
||||
console.log(req.files);
|
||||
if (!req.files || Object.keys(req.files).length === 0) {
|
||||
return res.status(400).send('No files were uploaded.');
|
||||
return res.status(400).send('No files were uploaded');
|
||||
}
|
||||
|
||||
const file: UploadedFile = req.files.formFile as UploadedFile;
|
||||
|
@ -7,7 +7,6 @@ import jsonImportRoute from './import/jsonImport.js';
|
||||
import categoryManager from './categoryManager.js';
|
||||
import storageManager from './storageManager.js';
|
||||
import startpageRoute from './startpage.js';
|
||||
import demoLoginRoute from './demo.js'
|
||||
|
||||
// Router base is '/manage'
|
||||
const Router = express.Router({ strict: false });
|
||||
@ -17,7 +16,6 @@ Router.route('/categories').get(categoryManager.get);
|
||||
Router.route('/storages').get(storageManager.get);
|
||||
Router.route('/import/csv').get(csvImportRoute.get).post(csvImportRoute.post);
|
||||
Router.route('/import/json').get(jsonImportRoute.get).post(jsonImportRoute.post);
|
||||
Router.route('/demo/login').get(demoLoginRoute.get);
|
||||
Router.route('/').get(startpageRoute.get);
|
||||
|
||||
export default Router;
|
||||
|
9
src/routes/frontend/projects/dashboard.ts
Normal file
9
src/routes/frontend/projects/dashboard.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import { prisma, __path, log } from '../../../index.js';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
res.render(__path + '/src/frontend/projects/dashboard.eta.html');
|
||||
|
||||
}
|
||||
|
||||
export default { get };
|
11
src/routes/frontend/projects/index.ts
Normal file
11
src/routes/frontend/projects/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import express from 'express';
|
||||
|
||||
// Route imports
|
||||
import dashboard from './dashboard.js';
|
||||
|
||||
// Router base is '/manage'
|
||||
const Router = express.Router({ strict: false });
|
||||
|
||||
Router.route('/').get(dashboard.get);
|
||||
|
||||
export default Router;
|
@ -1,16 +1,25 @@
|
||||
import express, { Express } from 'express';
|
||||
import { __path, prisma } from '../index.js';
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
||||
// Middleware Imports
|
||||
import { checkAuthentication } from '../middleware/auth.mw.js'
|
||||
|
||||
// Route imports
|
||||
import frontend_routes from './frontend/index.js';
|
||||
import static_routes from './static/index.js';
|
||||
import api_routes from './api/index.js';
|
||||
import auth_routes from './auth/index.js';
|
||||
|
||||
const Router = express.Router({ strict: false });
|
||||
|
||||
Router.use('/static', static_routes);
|
||||
Router.use('/api', api_routes);
|
||||
Router.use('/', frontend_routes);
|
||||
Router.use('/api', checkAuthentication, api_routes);
|
||||
Router.use('/auth', auth_routes);
|
||||
Router.use('/', checkAuthentication, frontend_routes);
|
||||
|
||||
// The error handler must be before any other error middleware and after all controllers
|
||||
Router.use(Sentry.Handlers.errorHandler());
|
||||
|
||||
// Default route.
|
||||
Router.all('*', function (req, res) {
|
||||
@ -23,3 +32,4 @@ Router.all('*', function (req, res) {
|
||||
});
|
||||
|
||||
export default Router;
|
||||
|
||||
|
@ -5,18 +5,31 @@ body {
|
||||
.headLogo {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Give the logo a dark shadow to make it pop out */
|
||||
.headLogo {
|
||||
filter: drop-shadow(0 0 0.85rem rgba(0, 0, 0, 0.35));
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.headLogo {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Safari */
|
||||
@media not all and (min-resolution: 0.001dpcm) {
|
||||
.versionInfo {
|
||||
font-size: 0.75rem;
|
||||
|
||||
}
|
||||
/** Safari */
|
||||
|
||||
@media not all and (min-resolution:.001dpcm) {
|
||||
@supports (-webkit-appearance:none) and (stroke-color:transparent) {
|
||||
.headLogo {
|
||||
width: 3%;
|
||||
width: 1%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
@ -28,10 +41,13 @@ body {
|
||||
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100; /* Behind the navbar */
|
||||
padding: 48px 0 0; /* Height of navbar */
|
||||
z-index: 100;
|
||||
/* Behind the navbar */
|
||||
padding: 48px 0 0;
|
||||
/* Height of navbar */
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/*
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar {
|
||||
@ -45,7 +61,8 @@ body {
|
||||
height: calc(100vh - 48px);
|
||||
padding-top: 0.5rem;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
overflow-y: auto;
|
||||
/* Scrollable contents if viewport is shorter than content. */
|
||||
}
|
||||
|
||||
.sidebar .nav-link {
|
||||
@ -120,18 +137,22 @@ body {
|
||||
transform: rotate(-90deg) !important;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.rotate::before {
|
||||
transform: rotate(-90deg) !important;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.derotate {
|
||||
transform: rotate(0deg) !important;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.derotate::before {
|
||||
transform: rotate(0deg) !important;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.dropdownIndicator {
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ function randomInRange(min, max) {
|
||||
|
||||
function doTheConfetti() {
|
||||
confetti({
|
||||
angle: 100,
|
||||
angle: randomInRange(90, 110),
|
||||
spread: randomInRange(70, 120),
|
||||
particleCount: randomInRange(100, 200),
|
||||
origin: { y: 0.6 }
|
||||
origin: { y: 0.6, x: randomInRange(0.4, 0.8) },
|
||||
});
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
const FLAG_supports_new_data_loader = true;
|
||||
|
||||
function getDataForEdit(name) {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: `/api/v1/categories?name=${name}`,
|
||||
success: function (data) {
|
||||
const result = JSON.parse(data);
|
||||
|
||||
success: function (result) {
|
||||
// Get elements inside the editCategoryModal
|
||||
const modal_categoryName = document.getElementById('editCategoryModalName');
|
||||
const modal_categoryDescription = document.getElementById('editCategoryModalDescription');
|
||||
@ -39,3 +39,42 @@ function primeEdit() {
|
||||
form.setAttribute('method', 'PATCH');
|
||||
return true;
|
||||
}
|
||||
|
||||
const itemList = $('#itemList');
|
||||
// itemList.empty();
|
||||
itemList.bootstrapTable({ url: '/api/v1/categories', search: true, showRefresh: true, responseHandler: dataResponseHandler, sidePagination: 'server', serverSort: true, silentSort: false });
|
||||
setTimeout(() => {
|
||||
activateTooltips();
|
||||
}, 1000);
|
||||
|
||||
function loadPageData() {
|
||||
itemList.bootstrapTable('refresh')
|
||||
setTimeout(() => {
|
||||
$(".tooltip").tooltip("hide");
|
||||
activateTooltips();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function dataResponseHandler(json) {
|
||||
// console.log(json)
|
||||
totalNotFiltered = json.totalNotFiltered;
|
||||
total = json.total;
|
||||
json = json.items;
|
||||
json.forEach((item) => {
|
||||
item.actions = `
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#editCategoryModal" onclick="primeEdit(); getDataForEdit('${item.name}')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="preFillDeleteModalNxt('${item.name}','categories','Category','name')" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>`
|
||||
});
|
||||
///// --------------------------------- /////
|
||||
setTimeout(() => {
|
||||
activateTooltips();
|
||||
}, 200);
|
||||
return {"rows": json, total: total, totalNotFiltered: totalNotFiltered, totalRows: total};
|
||||
|
||||
}
|
||||
|
||||
loadPageData()
|
||||
|
@ -19,8 +19,7 @@ function getDataForEdit(id) {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: `/api/v1/items?id=${id}`,
|
||||
success: function (data) {
|
||||
const result = JSON.parse(data);
|
||||
success: function (result) {
|
||||
|
||||
// Get elements inside the editCategoryModal
|
||||
const modal_itemName = document.getElementById('itemModifyModalName');
|
||||
@ -42,6 +41,7 @@ function getDataForEdit(id) {
|
||||
|
||||
// Select the correct option in the dropdown
|
||||
const modal_itemCategoryOptions = modal_itemCategory.options;
|
||||
modal_itemCategoryOptions[0].selected = true;
|
||||
for (let i = 0; i < modal_itemCategoryOptions.length; i++) {
|
||||
if (modal_itemCategoryOptions[i].value == result.categoryId) {
|
||||
modal_itemCategoryOptions[i].selected = true;
|
||||
@ -50,6 +50,7 @@ function getDataForEdit(id) {
|
||||
|
||||
// Select the correct option in the dropdown
|
||||
const modal_itemStatusOptions = modal_itemStatus.options;
|
||||
modal_itemStatusOptions[0].selected = true;
|
||||
for (let i = 0; i < modal_itemStatusOptions.length; i++) {
|
||||
if (modal_itemStatusOptions[i].value == result.statusId) {
|
||||
modal_itemStatusOptions[i].selected = true;
|
||||
@ -58,6 +59,7 @@ function getDataForEdit(id) {
|
||||
|
||||
// Select the correct option in the dropdown
|
||||
const modal_itemStorageLocationOptions = modal_itemStorageLocation.options;
|
||||
modal_itemStorageLocationOptions[0].selected = true;
|
||||
for (let i = 0; i < modal_itemStorageLocationOptions.length; i++) {
|
||||
if (modal_itemStorageLocationOptions[i].value == result.storageLocationId) {
|
||||
modal_itemStorageLocationOptions[i].selected = true;
|
||||
|
@ -2,6 +2,8 @@
|
||||
// This magic js codes enables anchor links to work with bootstrap tabs
|
||||
// Taken from https://stackoverflow.com/a/9393768/11317151 (and edited, like a lot)
|
||||
|
||||
const FLAG_supports_new_data_loader = true;
|
||||
|
||||
// Also update on location change
|
||||
window.addEventListener(
|
||||
'hashchange',
|
||||
@ -29,9 +31,9 @@ function primeCreateNew() {
|
||||
const form = document.getElementById('storageUnitModalForm');
|
||||
const form2 = document.getElementById('storageLocationModalForm');
|
||||
document.getElementById('createNewLocationSelection').disabled = false;
|
||||
document.getElementById('storageUnitModalLocationSelectText').innerText= "Select or create a new location.";
|
||||
document.getElementById('storageUnitModalLabel').innerText = "Create new storage unit";
|
||||
document.getElementById('storageLocationModalTitle').innerText = "Create new storage location";
|
||||
document.getElementById('storageUnitModalLocationSelectText').innerText = 'Select or create a new location.';
|
||||
document.getElementById('storageUnitModalLabel').innerText = 'Create new storage unit';
|
||||
document.getElementById('storageLocationModalTitle').innerText = 'Create new storage location';
|
||||
form.setAttribute('method', 'POST');
|
||||
form2.setAttribute('method', 'POST');
|
||||
return true;
|
||||
@ -42,25 +44,25 @@ function primeEdit() {
|
||||
const form2 = document.getElementById('storageLocationModalForm');
|
||||
// Disable create new location
|
||||
document.getElementById('createNewLocationSelection').disabled = true;
|
||||
document.getElementById('storageUnitModalLocationSelectText').innerText= "While editing you can only select already existing locations. Use the settings to create new ones.";
|
||||
document.getElementById('storageUnitModalLabel').innerText = "Edit a storage unit";
|
||||
document.getElementById('storageLocationModalTitle').innerText = "Edit a storage location"
|
||||
document.getElementById('storageUnitModalLocationSelect').selectedIndex = 1
|
||||
handleSelector()
|
||||
document.getElementById('storageUnitModalLocationSelectText').innerText = 'While editing you can only select already existing locations. Use the settings to create new ones.';
|
||||
document.getElementById('storageUnitModalLabel').innerText = 'Edit a storage unit';
|
||||
document.getElementById('storageLocationModalTitle').innerText = 'Edit a storage location';
|
||||
document.getElementById('storageUnitModalLocationSelect').selectedIndex = 1;
|
||||
handleSelector();
|
||||
form.setAttribute('method', 'PATCH');
|
||||
form2.setAttribute('method', 'PATCH');
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleSelector(){
|
||||
const selector = document.getElementById('storageUnitModalLocationSelect')
|
||||
function handleSelector() {
|
||||
const selector = document.getElementById('storageUnitModalLocationSelect');
|
||||
const value = selector.options[selector.selectedIndex].value;
|
||||
if(value == "META_CREATENEW") {
|
||||
$('#storageUnitModalContactInfoCreator').removeClass('d-none')
|
||||
$('.requireOnCreate').attr('required', true)
|
||||
if (value == 'META_CREATENEW') {
|
||||
$('#storageUnitModalContactInfoCreator').removeClass('d-none');
|
||||
$('.requireOnCreate').attr('required', true);
|
||||
} else {
|
||||
$('#storageUnitModalContactInfoCreator').addClass('d-none')
|
||||
$('.requireOnCreate').attr('required', false)
|
||||
$('#storageUnitModalContactInfoCreator').addClass('d-none');
|
||||
$('.requireOnCreate').attr('required', false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,9 +70,7 @@ function getDataForEdit(id) {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: `/api/v1/storageUnits?id=${id}`,
|
||||
success: function (data) {
|
||||
const result = JSON.parse(data);
|
||||
|
||||
success: function (result) {
|
||||
// Get elements inside the editCategoryModal
|
||||
const modal_unitName = document.getElementById('storageUnitModalName');
|
||||
const modal_unitLocation = document.getElementById('storageUnitModalLocationSelect');
|
||||
@ -81,15 +81,13 @@ function getDataForEdit(id) {
|
||||
modal_unitId.value = result.id;
|
||||
|
||||
// Select the correct location from the select based on the value of the option
|
||||
for(var i, j = 0; i = modal_unitLocation.options[j]; j++) {
|
||||
if(i.value == result.contactInfoId) {
|
||||
console.log("Found it");
|
||||
for (var i, j = 0; (i = modal_unitLocation.options[j]); j++) {
|
||||
if (i.value == result.contactInfoId) {
|
||||
console.log('Found it');
|
||||
modal_unitLocation.selectedIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
error: function (data) {
|
||||
console.log('!!!! ERROR !!!!', data);
|
||||
@ -97,19 +95,16 @@ function getDataForEdit(id) {
|
||||
$('.loader-overlay').removeClass('active');
|
||||
// Close the modal
|
||||
$('.modal').modal('hide');
|
||||
createNewToast('<i class="bi bi-exclamation-triangle-fill"></i> Something went wrong. The storage unit does no longer exist.', "text-bg-danger")
|
||||
createNewToast('<i class="bi bi-exclamation-triangle-fill"></i> Something went wrong. The storage unit does no longer exist.', 'text-bg-danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getDataForEditLoc(id) {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: `/api/v1/storageLocations?id=${id}`,
|
||||
success: function (data) {
|
||||
const result = JSON.parse(data);
|
||||
|
||||
success: function (result) {
|
||||
// Get elements inside the editCategoryModal
|
||||
const modal_locationName = document.getElementById('storageLocationModalName');
|
||||
const modal_locationUnitSel = document.getElementById('storageLocationModalUnit');
|
||||
@ -120,15 +115,13 @@ function getDataForEditLoc(id) {
|
||||
modal_locationId.value = result.id;
|
||||
|
||||
// Select the correct location from the select based on the value of the option
|
||||
for(var i, j = 0; i = modal_locationUnitSel.options[j]; j++) {
|
||||
if(i.value == result.storageUnitId) {
|
||||
console.log("Found it");
|
||||
for (var i, j = 0; (i = modal_locationUnitSel.options[j]); j++) {
|
||||
if (i.value == result.storageUnitId) {
|
||||
console.log('Found it');
|
||||
modal_locationUnitSel.selectedIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
error: function (data) {
|
||||
console.log('!!!! ERROR !!!!', data);
|
||||
@ -136,9 +129,89 @@ function getDataForEditLoc(id) {
|
||||
$('.loader-overlay').removeClass('active');
|
||||
// Close the modal
|
||||
$('.modal').modal('hide');
|
||||
createNewToast('<i class="bi bi-exclamation-triangle-fill"></i> Something went wrong. The storage unit does no longer exist.', "text-bg-danger")
|
||||
createNewToast('<i class="bi bi-exclamation-triangle-fill"></i> Something went wrong. The storage unit does no longer exist.', 'text-bg-danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleSelector()
|
||||
const itemList = $('#itemList');
|
||||
const itemListUnit = $('#itemListUnit');
|
||||
|
||||
// itemList.empty();
|
||||
itemListUnit.bootstrapTable({ url: '/api/v1/storageUnits', search: true, showRefresh: true, responseHandler: dataResponseHandlerUnit, sidePagination: 'server', serverSort: true, silentSort: false });
|
||||
itemList.bootstrapTable({ url: '/api/v1/storageLocations', search: true, showRefresh: true, responseHandler: dataResponseHandler, sidePagination: 'server', serverSort: true, silentSort: false });
|
||||
setTimeout(() => {
|
||||
activateTooltips();
|
||||
}, 1000);
|
||||
|
||||
function loadPageData() {
|
||||
// itemList.empty();
|
||||
itemList.bootstrapTable('refresh');
|
||||
itemListUnit.bootstrapTable('refresh');
|
||||
|
||||
setTimeout(() => {
|
||||
$(".tooltip").tooltip("hide");
|
||||
activateTooltips();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function dataResponseHandler(json) {
|
||||
// console.log(json)
|
||||
totalNotFiltered = json.totalNotFiltered;
|
||||
total = json.total;
|
||||
json = json.items;
|
||||
json.forEach((item) => {
|
||||
colorStatus = '';
|
||||
item.actions = `
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#storageLocationModal" onclick="primeEdit(); getDataForEditLoc('${item.id}')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="preFillDeleteModalNxt('${item.id}','storageLocations','Storage Location')" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>`;
|
||||
if (item.storageUnit == null) {
|
||||
item.storageUnit = '<i>No storage unit assigned</i>';
|
||||
} else {
|
||||
item.storageUnit = item.storageUnit.name;
|
||||
console.log(item.storageUnit);
|
||||
}
|
||||
|
||||
// item.SKU = `<p data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="ID: ${item.id}">${item.SKU}</p>`
|
||||
});
|
||||
///// --------------------------------- /////
|
||||
setTimeout(() => {
|
||||
activateTooltips();
|
||||
}, 200);
|
||||
return { rows: json, total: total, totalNotFiltered: totalNotFiltered, totalRows: total };
|
||||
}
|
||||
|
||||
function dataResponseHandlerUnit(json) {
|
||||
// console.log(json)
|
||||
totalNotFiltered = json.totalNotFiltered;
|
||||
total = json.total;
|
||||
json = json.items;
|
||||
json.forEach((item) => {
|
||||
colorStatus = '';
|
||||
item.actions = `
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#storageUnitModal" onclick="primeEdit(); getDataForEdit('${item.id}')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="preFillDeleteModalNxt('${item.id}','storageUnits','Storage Unit')" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>`;
|
||||
if (item.contactInfo == null) {
|
||||
item.address = '<i>No address assigned</i>';
|
||||
} else {
|
||||
item.address = `${item.contactInfo.street} ${item.contactInfo.houseNumber}, ${item.contactInfo.city} ${item.contactInfo.country}`;
|
||||
}
|
||||
|
||||
// item.SKU = `<p data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="ID: ${item.id}">${item.SKU}</p>`
|
||||
});
|
||||
///// --------------------------------- /////
|
||||
setTimeout(() => {
|
||||
activateTooltips();
|
||||
}, 200);
|
||||
return { rows: json, total: total, totalNotFiltered: totalNotFiltered, totalRows: total };
|
||||
}
|
||||
|
||||
handleSelector();
|
||||
|
@ -1,4 +1,13 @@
|
||||
var amountOfForms = $('.frontendForm').length;
|
||||
|
||||
function isNewDataLoaderAvailable() {
|
||||
try {
|
||||
return FLAG_supports_new_data_loader;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$('.frontendForm').each(function () {
|
||||
// TODO Handle empty strings as null or undefined, not as ''
|
||||
$(this).on('submit', function (e) {
|
||||
@ -26,7 +35,12 @@ $('.frontendForm').each(function () {
|
||||
// Clear all fields
|
||||
form.find('input, textarea').val('');
|
||||
// Create toast
|
||||
if(isNewDataLoaderAvailable()) {
|
||||
createNewToast('<i class="bi bi-check2"></i> Changes saved successfully.', "text-bg-success", undefined, false)
|
||||
} else {
|
||||
createNewToast('<i class="bi bi-check2"></i> Changes saved successfully.', "text-bg-success")
|
||||
}
|
||||
|
||||
},
|
||||
error: function (data) {
|
||||
console.log('error');
|
||||
@ -57,7 +71,12 @@ function deleteEntryNxt(id, route, name) {
|
||||
data: { id: id },
|
||||
success: function (data) {
|
||||
$('#staticBackdrop').modal('hide');
|
||||
if(isNewDataLoaderAvailable()) {
|
||||
createNewToast(`<i class="bi bi-check2"></i> ${name} deleted successfully.`, "text-bg-success", undefined, false)
|
||||
} else {
|
||||
createNewToast(`<i class="bi bi-check2"></i> ${name} deleted successfully.`, "text-bg-success")
|
||||
}
|
||||
|
||||
|
||||
confetti({
|
||||
spread: 360,
|
||||
@ -94,8 +113,7 @@ function preFillDeleteModalNxt(id, route, name, requestIdent='id') {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: `/api/v1/${route}?${requestIdent}=${id}`,
|
||||
success: function (data) {
|
||||
const result = JSON.parse(data);
|
||||
success: function (result) {
|
||||
|
||||
// Get elements inside the editCategoryModal
|
||||
const modal_categoryName = document.getElementById('deleteNamePlaceholder');
|
||||
|
@ -10,7 +10,6 @@ trinagles.each(function () {
|
||||
$(this).addClass('rotate');
|
||||
}
|
||||
|
||||
console.log('target', target);
|
||||
target.on('show.bs.collapse', function () {
|
||||
$(triTar).addClass('rotate');
|
||||
$(triTar).removeClass('derotate');
|
||||
|
65
static/js/itemPageHandler.js
Normal file
65
static/js/itemPageHandler.js
Normal file
@ -0,0 +1,65 @@
|
||||
const FLAG_supports_new_data_loader = true;
|
||||
|
||||
/**
|
||||
* Should we ever implement items in items, have a look at this:
|
||||
* https://examples.bootstrap-table.com/index.html?extensions/treegrid.html#extensions/treegrid.html
|
||||
*/
|
||||
|
||||
// Inital thing
|
||||
const itemList = $('#itemList');
|
||||
itemList.bootstrapTable({ url: '/api/v1/items', search: true, showRefresh: true, responseHandler: dataResponseHandler, sidePagination: 'server', serverSort: true, silentSort: false });
|
||||
setTimeout(() => {
|
||||
activateTooltips();
|
||||
}, 1000);
|
||||
|
||||
function loadPageData() {
|
||||
itemList.bootstrapTable('refresh')
|
||||
setTimeout(() => {
|
||||
$(".tooltip").tooltip("hide");
|
||||
activateTooltips();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function dataResponseHandler(json) {
|
||||
// console.log(json)
|
||||
totalNotFiltered = json.totalNotFiltered;
|
||||
total = json.total;
|
||||
json = json.items;
|
||||
json.forEach((item) => {
|
||||
colorStatus = '';
|
||||
if(item.SKU == null) item.SKU = '<i>No SKU assigned</i>';
|
||||
switch (item.status) {
|
||||
case 'normal':
|
||||
colorStatus = 'success';
|
||||
break;
|
||||
case 'stolen':
|
||||
colorStatus = 'danger';
|
||||
break;
|
||||
case 'lost':
|
||||
colorStatus = 'warning';
|
||||
break;
|
||||
case 'borrowed':
|
||||
colorStatus = 'info';
|
||||
break;
|
||||
default:
|
||||
colorStatus = 'secondary';
|
||||
}
|
||||
item.status = `<span class="badge text-bg-${colorStatus}">${item.status}</span>`;
|
||||
item.actions = `
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModifyModal" onclick="primeEdit(); getDataForEdit('${item.id}')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="preFillDeleteModalNxt('${item.id}','items','Item')" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>`
|
||||
item.SKU = `<p data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="ID: ${item.id}">${item.SKU}</p>`
|
||||
});
|
||||
///// --------------------------------- /////
|
||||
setTimeout(() => {
|
||||
activateTooltips();
|
||||
}, 200);
|
||||
return {"rows": json, total: total, totalNotFiltered: totalNotFiltered, totalRows: total};
|
||||
|
||||
}
|
||||
|
||||
loadPageData()
|
@ -60,15 +60,14 @@ function handleSearchChange(e) {
|
||||
autocompleteBox.innerHTML = 'Start typing to search for commands <br> #SKU';
|
||||
return;
|
||||
}
|
||||
const baseURI = window.location.origin;
|
||||
const baseURI = window.location.origin; // move to new fancy route
|
||||
const url = baseURI + '/api/v1/search/sku?sku=' + searchedSKU;
|
||||
|
||||
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: url,
|
||||
success: function (data) {
|
||||
let result = JSON.parse(data);
|
||||
success: function (result) {
|
||||
let htmlResult = ""
|
||||
result.forEach(element => {
|
||||
console.log(element);
|
||||
|
@ -1,4 +1,11 @@
|
||||
currentToasts = [];
|
||||
var forceSkipReload = false;
|
||||
forceSkipReload = localStorage.getItem('forceSkipReload') === 'true';
|
||||
if(forceSkipReload) {
|
||||
setTimeout(() => {
|
||||
createNewToast('Auto reload still disabled, click version number to reenable.', 'text-bg-warning', 3000, false);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function to create a new toast
|
||||
@ -8,7 +15,7 @@ currentToasts = [];
|
||||
* @param {Boolean} autoReload Should the page reload after the toast is hidden, default is true (for compatibility with old code)
|
||||
* @returns {String} The id of the created toast, format: toast-<number>
|
||||
*/
|
||||
function createNewToast(message, colorSelector, autoHideTime = 3000, autoReload = true) {
|
||||
function createNewToast(message, colorSelector, autoHideTime = 1500, autoReload = true) {
|
||||
const targetContainer = document.getElementById('toastMainController');
|
||||
const masterToast = document.getElementById('masterToast');
|
||||
const newToast = masterToast.cloneNode(true);
|
||||
@ -19,9 +26,14 @@ function createNewToast(message, colorSelector, autoHideTime = 3000, autoReload
|
||||
targetContainer.appendChild(newToast);
|
||||
currentToasts.push(newToast);
|
||||
$(newToast).toast('show');
|
||||
try {
|
||||
loadPageData();
|
||||
} catch (error) {
|
||||
console.debug("Page does not support new data loading.")
|
||||
}
|
||||
setTimeout(() => {
|
||||
destroyToast(newToast.id);
|
||||
if (autoReload) {
|
||||
if (autoReload && !forceSkipReload) {
|
||||
location.reload();
|
||||
}
|
||||
}, autoHideTime);
|
||||
@ -48,3 +60,17 @@ function normalizeToast() {
|
||||
$('#generalToast').removeClass('text-bg-warning');
|
||||
$('#generalToast').removeClass('text-bg-info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to handle the "secret" function to globally disable auto reload
|
||||
*/
|
||||
function toggleAutoReload() {
|
||||
forceSkipReload = !forceSkipReload;
|
||||
if(forceSkipReload) {
|
||||
createNewToast('Auto reload disabled', 'text-bg-warning', 1500, false);
|
||||
} else {
|
||||
createNewToast('Auto reload enabled', 'text-bg-success', 1500, false);
|
||||
}
|
||||
// Store the value in local storage
|
||||
localStorage.setItem('forceSkipReload', forceSkipReload);
|
||||
}
|
35
tools/generate.js
Normal file
35
tools/generate.js
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* This file can be used to load-test the application
|
||||
*/
|
||||
|
||||
import { writeFileSync } from "fs";
|
||||
|
||||
// Temporary file to generate demo data for the inventory
|
||||
const inventoryItems = [];
|
||||
|
||||
// Function to add an item to the inventory using a simple function
|
||||
function addInventoryItem(name, amount, manufacturer, category, sku) {
|
||||
const item = {
|
||||
name: name,
|
||||
amount: amount,
|
||||
manufacturer: manufacturer,
|
||||
category: category,
|
||||
sku: sku
|
||||
};
|
||||
|
||||
inventoryItems.push(item);
|
||||
}
|
||||
|
||||
// Loop to generate 2000 items
|
||||
for (let i = 1; i <= 1024; i++) {
|
||||
const itemName = `Item ${i}`;
|
||||
const itemAmount = Math.floor(Math.random() * 100) + 1;
|
||||
const itemManufacturer = `Manufacturer ${i}`;
|
||||
const itemCategory = `Category ${i}`;
|
||||
const itemSKU = `SKU-${i}`;
|
||||
|
||||
addInventoryItem(itemName, itemAmount, itemManufacturer, itemCategory, itemSKU);
|
||||
}
|
||||
|
||||
// Save the generated data to a file
|
||||
writeFileSync('demoData.json', JSON.stringify(inventoryItems))
|
Reference in New Issue
Block a user