Compare commits
156 Commits
dd88f258ed
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| c35726bf5c | |||
| e82208fc5e | |||
| 9bdca3317c | |||
| 61abd44328 | |||
| acb9876d9b | |||
| 10d88f53eb | |||
| bba0aa2ee4 | |||
| b7773ec02a | |||
| b9dd6e3ae2 | |||
| f940bcdc9d | |||
| 2ae3c8ba59 | |||
| 97b9c4db4a | |||
| d8eddafcae | |||
| 58b58424ab | |||
| 72d52d9256 | |||
| 5dfece09b7 | |||
| 97eb73c6d1 | |||
| bb6d523abb | |||
| 09d3a7041a | |||
| 132e8d0146 | |||
| d0a0d08902 | |||
| 208cd615af | |||
| e82ef76316 | |||
| 4db4eea2d1 | |||
| edcfff6cc6 | |||
| 61860c759e | |||
| 08c9b3404f | |||
| 93ed2e9ee8 | |||
| fb9ee15bda | |||
| 76bc0db3dd | |||
| 2e5de34cb5 | |||
| 07e47e64ae | |||
| 73eb70538e | |||
| 8b8c9acb69 | |||
| cb3ab668d8 | |||
| f6654067f5 | |||
| 532a722763 | |||
| 38d1043606 | |||
| 18daca904e | |||
| e833c48cc8 | |||
| 0bd217c727 | |||
| 74974323f0 | |||
| f3c4c26ef9 | |||
| 74bcd6e805 | |||
| eb2c34c557 | |||
| 808e3d84ef | |||
| 7a22b52c91 | |||
| e17b8e7bbe | |||
| e597968777 | |||
| bbd7a17144 | |||
| 5945ace9f2 | |||
| 316b27e3e0 | |||
| 7165c959b9 | |||
| ae7e3fd54e | |||
| 7ec5c5beb0 | |||
| c9aa41ab00 | |||
| 4696835c65 | |||
| 2d3f308877 | |||
| 3d083986ae | |||
| 92b47f03d9 | |||
| 3b8f18093e | |||
| 1cd1d3a3e9 | |||
| 8324fffeb6 | |||
| 354f3260a5 | |||
| ab5af4deac | |||
| 7a15b160ac | |||
| ca0ba7fe59 | |||
| 0914b53341 | |||
| 34b80cf236 | |||
| 196ea00972 | |||
| 807a4ca699 | |||
| d98a659262 | |||
| 6dd5f4b61f | |||
| 751b9d5701 | |||
| b0634d9427 | |||
| 8a143fbb19 | |||
| 8b72a1b47e | |||
| f4c44fb02b | |||
| 03aa7e5aee | |||
| f035e6f14a | |||
| 227e2aa00c | |||
| 4a48dd2bb5 | |||
| 9796947db5 | |||
| 1e02d4fa4f | |||
| 26ea8d5e89 | |||
| 8fb06c71d3 | |||
| e258aaead9 | |||
| 491de26a0b | |||
| f1979f0fc9 | |||
| f4433f4192 | |||
| bff0e77766 | |||
| 43ca16dc80 | |||
| 121bed59fd | |||
| 4e885ce74c | |||
| 4908d6644a | |||
| 21c374903b | |||
| ed11b2b26f | |||
| 357c118396 | |||
| 866b6d5120 | |||
| 739e6bbe9f | |||
| 0975f4e499 | |||
| 2990a330dd | |||
| cb59f58926 | |||
| 1a79a97929 | |||
| 4b92a28dfa | |||
| 4aae659ee4 | |||
| 7bfb48b81f | |||
| 7432decd3c | |||
| 04a607d3bc | |||
| d178e04362 | |||
| 020fdc83af | |||
| 33803a69a6 | |||
| 3ea6dc5bc4 | |||
| b484a0c5ea | |||
| a73c63cefa | |||
| 5b97d078c5 | |||
| 7529cc5b0c | |||
| df252e72b3 | |||
| 359427824b | |||
| cbdd218fe4 | |||
| 35acd2b0b3 | |||
| 625d5969be | |||
| 2370d45220 | |||
| cb7a4068fc | |||
| e4d15ef051 | |||
| f3fa617aa5 | |||
| ab16059387 | |||
| b6f1cfdc6c | |||
| 1ce5f9acf7 | |||
| 44da09d22c | |||
| 0b72ffa935 | |||
| a2fb845e2e | |||
| bcd65b4b91 | |||
| b1d25c7ba2 | |||
| c9a5203131 | |||
| 246b5a17bd | |||
| 245ba19cdc | |||
| e5e6aa3fee | |||
| 81eb10e021 | |||
| 51b81b97bc | |||
| 242494a4ea | |||
| 542c08f10b | |||
| 0578d28669 | |||
| 5696871b8d | |||
| 125f7f5046 | |||
| 1cb8b70430 | |||
| 200ec975f7 | |||
| 5f5757c784 | |||
| 571b74f0e7 | |||
| 8363cc2816 | |||
| d852e9e573 | |||
| bd4ca7ba63 | |||
| df85a30dff | |||
| d5e9176d53 | |||
| 45b1f7c0a6 | |||
| eaac8143e9 |
12
.gitignore
vendored
12
.gitignore
vendored
@@ -4,6 +4,12 @@ server/dist
|
||||
dist
|
||||
apiDocsLSTV2
|
||||
testFiles
|
||||
builds
|
||||
nssm.exe
|
||||
backend-0.1.2-217.zip
|
||||
backend-0.1.2-218.zip
|
||||
backend-0.1.2.zip
|
||||
postgresql-17.2-3-windows-x64.exe
|
||||
|
||||
|
||||
# ---> Node
|
||||
@@ -142,8 +148,4 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
|
||||
nssm.exe
|
||||
|
||||
backend-0.1.2-217.zip
|
||||
postgresql-17.2-3-windows-x64.exe
|
||||
backend-0.1.3.zip
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"types": [
|
||||
{"type": "feat", "section": "🌟 Enhancements"},
|
||||
{"type": "fix", "section": "🐛 Bug fixes"},
|
||||
{"type": "chore", "hidden": false, "section": "📝 Chore"},
|
||||
{"type": "docs", "section": "📚 Documentation"},
|
||||
{"type": "style", "hidden": true},
|
||||
{"type": "refactor", "section": "🛠️ Code Refactor"},
|
||||
{"type": "perf", "hidden": false, "section": "🚀 Performance"},
|
||||
{"type": "test", "section": "📝 Testing Code"},
|
||||
{"type": "ci", "section": "📈 Project changes"}
|
||||
],
|
||||
"commitUrlFormat": "https://git.tuffraid.net/cowch/lstV2/commits/{{hash}}",
|
||||
"compareUrlFormat": "https://git.tuffraid.net/cowch/lstV2/compare/{{previousTag}}...{{currentTag}}",
|
||||
"header": "# All CHanges to LST can be found below.\n"
|
||||
"types": [
|
||||
{ "type": "feat", "section": "🌟 Enhancements" },
|
||||
{ "type": "fix", "section": "🐛 Bug fixes" },
|
||||
{ "type": "chore", "hidden": false, "section": "📝 Chore" },
|
||||
{ "type": "docs", "section": "📚 Documentation" },
|
||||
{ "type": "style", "hidden": true },
|
||||
{ "type": "refactor", "section": "🛠️ Code Refactor" },
|
||||
{ "type": "perf", "hidden": false, "section": "🚀 Performance" },
|
||||
{ "type": "test", "section": "📝 Testing Code" },
|
||||
{ "type": "ci", "hidden": false, "section": "📈 Project changes" },
|
||||
{ "type": "build", "hidden": true, "section": "📈 Project Builds" }
|
||||
],
|
||||
"commitUrlFormat": "https://git.tuffraid.net/cowch/lstV2/commits/{{hash}}",
|
||||
"compareUrlFormat": "https://git.tuffraid.net/cowch/lstV2/compare/{{previousTag}}...{{currentTag}}",
|
||||
"header": "# All CHanges to LST can be found below.\n"
|
||||
}
|
||||
|
||||
26
.vscode/settings.json
vendored
Normal file
26
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[json]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[graphql]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[handlebars]": {
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
||||
285
CHANGELOG.md
285
CHANGELOG.md
@@ -1,5 +1,290 @@
|
||||
# All CHanges to LST can be found below.
|
||||
|
||||
## [2.9.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.8.0...v2.9.0) (2025-03-23)
|
||||
|
||||
|
||||
### 📈 Project changes
|
||||
|
||||
* **frontend:** added ocme as a proxy in the dev ([eb2c34c](https://git.tuffraid.net/cowch/lstV2/commits/eb2c34c557b72c3387b70addac30a4d8291402d4))
|
||||
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
* **install:** added more env params and an install install stuff ([09d3a70](https://git.tuffraid.net/cowch/lstV2/commits/09d3a7041a18283a5add86ea845a8d7249522769))
|
||||
* **install:** removed the super secret key ([357c118](https://git.tuffraid.net/cowch/lstV2/commits/357c1183964be0f3c02207c24bc8f83347282211))
|
||||
* **logs:** changes how logs are put into the db they will be there name vs key ([18daca9](https://git.tuffraid.net/cowch/lstV2/commits/18daca904e0305371c6128988b48d54e3aec9a2a))
|
||||
* **migration:** added more documents on the install md ([0bd217c](https://git.tuffraid.net/cowch/lstV2/commits/0bd217c727d726d62ddf06a0e44e95c4606361cf))
|
||||
|
||||
|
||||
### 📝 Testing Code
|
||||
|
||||
* **ocme:** cycle count intital improvements ([7a22b52](https://git.tuffraid.net/cowch/lstV2/commits/7a22b52c916335c748598b82e78819c325bb8c4c))
|
||||
* **ocp dash:** more work on the dashboard ([f3c4c26](https://git.tuffraid.net/cowch/lstV2/commits/f3c4c26ef957e6fe359a77f139ad36cdacefb8ef))
|
||||
* **ocp:** more work on the dashboard ([8324fff](https://git.tuffraid.net/cowch/lstV2/commits/8324fffeb664bff3751ffc26e988a4a506b658a9))
|
||||
* **returnres:** tryed to make a standard return res but will come back to this later ([58b5842](https://git.tuffraid.net/cowch/lstV2/commits/58b58424abbf98feec0c5442a59f4d055bbd1811))
|
||||
* **rfid:** more work on the rfid service ([21c3749](https://git.tuffraid.net/cowch/lstV2/commits/21c374903b5cba56ced44b7eb2066ee902fc7bc3))
|
||||
* **server:** start/stop/restart buttons added. comment [#14](https://git.tuffraid.net/cowch/lstV2/issues/14) ([76bc0db](https://git.tuffraid.net/cowch/lstV2/commits/76bc0db3dd9c48b69e4f6f571181a1f07c48bdeb))
|
||||
* **streaming logs:** more test but failed again ([08c9b34](https://git.tuffraid.net/cowch/lstV2/commits/08c9b3404f548a167464445ec2f19288a092b0dc))
|
||||
* **streaming logs:** test for streaming logs ([73eb705](https://git.tuffraid.net/cowch/lstV2/commits/73eb70538e36601cf436aaba244f99d5bd873d34))
|
||||
* **streaming logs:** will come back to this later this is killing me inside ([97eb73c](https://git.tuffraid.net/cowch/lstV2/commits/97eb73c6d1aef747ef98f5f666fe47622795746f))
|
||||
* **streaming:** more streaming test ([4db4eea](https://git.tuffraid.net/cowch/lstV2/commits/4db4eea2d12e37809879e96ed4279d6e23e83acc))
|
||||
* **streaming:** more testing on streaming the lofs ([e82ef76](https://git.tuffraid.net/cowch/lstV2/commits/e82ef76316f0562b57124eb977c86fd2c6a3f332))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **admincheck:** this check is so we dont use stuff on the wrong servers ([3d08398](https://git.tuffraid.net/cowch/lstV2/commits/3d083986aed528c2f881d6f673c0be03b8986c0b))
|
||||
* **auth:** add, update were added for adm account in backend only ([d8eddaf](https://git.tuffraid.net/cowch/lstV2/commits/d8eddafcaea9141e9413a122a320649c7dd325e5))
|
||||
* **auth:** remove all old localstorage if no session ([8fb06c7](https://git.tuffraid.net/cowch/lstV2/commits/8fb06c71d370a27697628077474a261596b567e0))
|
||||
* **db:** logs, manualprints added ([0914b53](https://git.tuffraid.net/cowch/lstV2/commits/0914b5334119fa705eedddc6c9e8303cb82da551))
|
||||
* **installer:** added a check for lstv2 already installed ([4696835](https://git.tuffraid.net/cowch/lstV2/commits/4696835c6557c2eae3be6a292144493be9d98f67))
|
||||
* **logger:** logger service created with its endpoints ([7ec5c5b](https://git.tuffraid.net/cowch/lstV2/commits/7ec5c5beb0a9b02e8b8761908ee75ac2cfd317ee))
|
||||
* **logger:** streaming logs works server side not frontend for now ([e833c48](https://git.tuffraid.net/cowch/lstV2/commits/e833c48cc8f68af40888ea161100e02f6ad19604))
|
||||
* **lst:** added in delay global function ([7497432](https://git.tuffraid.net/cowch/lstV2/commits/74974323f0a7bd7c2eaa22024b7a8e91db2868e2))
|
||||
* **lst:** added prettier config so we have the same formatting across all computers ([132e8d0](https://git.tuffraid.net/cowch/lstV2/commits/132e8d0146318fa9deb673bbcabf0d80c2a6d39f))
|
||||
* **ocme:** added in ocme service so we can utlize 2 ports ([ae7e3fd](https://git.tuffraid.net/cowch/lstV2/commits/ae7e3fd54e5c256fb82e68f2e935e3b914e43d13))
|
||||
* **ocme:** cycle count implemeneted ([74bcd6e](https://git.tuffraid.net/cowch/lstV2/commits/74bcd6e805c34e181f0c48310a799daf3afaee66))
|
||||
* **ocmeserver:** the server was just migrated so it can be upgraded to lstv2 ([e258aae](https://git.tuffraid.net/cowch/lstV2/commits/e258aaead9a56cc4de36c17ed60b0bdaa91019cb))
|
||||
* **ocp:** added in service plus manual print log ([7165c95](https://git.tuffraid.net/cowch/lstV2/commits/7165c959b9d5b37c0d4b01cb3f5bc27b40cec71d))
|
||||
* **ports:** added in production port if in production ([121bed5](https://git.tuffraid.net/cowch/lstV2/commits/121bed59fda04b2ad134feb0895428515a1c56d8))
|
||||
* **scripts:** made moving scripts more proper ([d0a0d08](https://git.tuffraid.net/cowch/lstV2/commits/d0a0d0890255acf890ba402386847b19566c92df))
|
||||
* **serverdata:** added in huston, sherman. and corrected contact info in westbend ([4908d66](https://git.tuffraid.net/cowch/lstV2/commits/4908d6644a392395e660d382536c0fd31e3d0647))
|
||||
* **server:** ocpService and loggerService added ([2d3f308](https://git.tuffraid.net/cowch/lstV2/commits/2d3f30887744bddfe22dc764188cf727a5e476df))
|
||||
* **servers:** added dayton in ([bbd7a17](https://git.tuffraid.net/cowch/lstV2/commits/bbd7a17144e1e1a0faa192139cb1539c2f1ecc5c))
|
||||
* **settings:** added in setting store ([1cd1d3a](https://git.tuffraid.net/cowch/lstV2/commits/1cd1d3a3e9e1ec1bf16ac2552caf24aa299959bf))
|
||||
* **settings:** more seed settings ([e597968](https://git.tuffraid.net/cowch/lstV2/commits/e597968777e88289d361fdc01ec683a1f4c80192))
|
||||
* **trycatch:** added in theo's try catch to reduce the code and love it ([72d52d9](https://git.tuffraid.net/cowch/lstV2/commits/72d52d925677eeeac6d158028114201862b6e2a2))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **auth:** added in correct bycrptjs ([208cd61](https://git.tuffraid.net/cowch/lstV2/commits/208cd615af8250686745aa5b6779706ae267e423))
|
||||
* **auth:** moved prod back to server as we run 2 instances during migration ([7a15b16](https://git.tuffraid.net/cowch/lstV2/commits/7a15b160ac2393cd66a932e598ffaa5aeda5812f))
|
||||
* **consume materail:** get token from localstorage as the store isnt wokring properly ([354f326](https://git.tuffraid.net/cowch/lstV2/commits/354f3260a55b53bfb461a37db9d64550bacbfe04))
|
||||
* **frontend:** added date-fns into the frontend ([bba0aa2](https://git.tuffraid.net/cowch/lstV2/commits/bba0aa2ee4e9b9be1db184da894ce6e96fd2e38f))
|
||||
* **login:** removed all the data from teh login dropdown as it could cause issues ([5dfece0](https://git.tuffraid.net/cowch/lstV2/commits/5dfece09b7285dda96875bbc740df803d816b92a))
|
||||
* **login:** removed roles from the login to shrink the jwt ([c9aa41a](https://git.tuffraid.net/cowch/lstV2/commits/c9aa41ab0099b7a05d50d9a981cf7e8a42a04733))
|
||||
* **production:** changes ocp to viewwer ([ab5af4d](https://git.tuffraid.net/cowch/lstV2/commits/ab5af4deacbeaf1ed93c6231fb98b187f7540ca4))
|
||||
* **server query:** bumped the refresh from 500ms to 2500ms ([866b6d5](https://git.tuffraid.net/cowch/lstV2/commits/866b6d5120810252b089580d341f3fb1b62e951a))
|
||||
* **serverdata:** remapped the server list to all be on the E drive and deactivated ([8b8c9ac](https://git.tuffraid.net/cowch/lstV2/commits/8b8c9acb6969b63f157ea95b7d61923bf4bb4eae))
|
||||
* **server:** removed the websocket wrapper going wiht normal ws ([bb6d523](https://git.tuffraid.net/cowch/lstV2/commits/bb6d523abbd3c9423eddbaab96e297b4850e2aa8))
|
||||
* **settings:** refactored the admincheck so we can reuse it ([ca0ba7f](https://git.tuffraid.net/cowch/lstV2/commits/ca0ba7fe59f7e19d0de4924a419cce461de4b7a1))
|
||||
* **settings:** removed the need to login to get the settings ([5945ace](https://git.tuffraid.net/cowch/lstV2/commits/5945ace9f259f6ef418c1621c246982b5b572dc1))
|
||||
* **settings:** used the common response function created ([316b27e](https://git.tuffraid.net/cowch/lstV2/commits/316b27e3e011a0c0b4ce88ea579290807b8927c5))
|
||||
* **stores:** added in axios ([3b8f180](https://git.tuffraid.net/cowch/lstV2/commits/3b8f18093ead2a988b2b19d1e8f25db6eaeaaee8))
|
||||
* **view access:** if role [] then allow them to see it ([e17b8e7](https://git.tuffraid.net/cowch/lstV2/commits/e17b8e7bbe94c25c1bd3414b1db31191c87553d6))
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **auth:** added in the correct function for days between logins ([ed11b2b](https://git.tuffraid.net/cowch/lstV2/commits/ed11b2b26ff80fde9f94615f740eb5152b16744d))
|
||||
* **auth:** fixed the getaccess to be getuseraccess as it was orignally ([b9dd6e3](https://git.tuffraid.net/cowch/lstV2/commits/b9dd6e3ae2f5c158dd4827ae0c9db2c6747c57e1))
|
||||
* **calendar:** this component had a bug and needed a lib update ([93ed2e9](https://git.tuffraid.net/cowch/lstV2/commits/93ed2e9ee8c98edd6a8c47d6e7a0caf6a8e93278))
|
||||
* **consume material:** when we consumed material the button was never reenabled ([07e47e6](https://git.tuffraid.net/cowch/lstV2/commits/07e47e64ae2f4ddd325a2fdb34c82143c9adf84b)), closes [#15](https://git.tuffraid.net/cowch/lstV2/issues/15)
|
||||
* **frontend:** removed unwanted import ([10d88f5](https://git.tuffraid.net/cowch/lstV2/commits/10d88f53ebaf670c9f6b3ca59cbbaf4d88f26b9f))
|
||||
* **loginform:** removed the console log that was left by accident ([2ae3c8b](https://git.tuffraid.net/cowch/lstV2/commits/2ae3c8ba5916b5249135b86b374ac1bf32837478))
|
||||
* **login:** if we have a wrong password or username we didnt properly error instead we crashed ([2e5de34](https://git.tuffraid.net/cowch/lstV2/commits/2e5de34cb50c79cd60e038492b2397eee69def11))
|
||||
* **scaler:** fix due to update ([edcfff6](https://git.tuffraid.net/cowch/lstV2/commits/edcfff6cc6cf81e56b5532f0876383659b951a5d))
|
||||
* **serverlist:** corrected the time by removing teh Z at the end of the time ([f940bcd](https://git.tuffraid.net/cowch/lstV2/commits/f940bcdc9df2d3e9989d49feae88036a6f8c7013))
|
||||
* **sqlserver:** if we already have a connection just return we dont want to try a second time ([b7773ec](https://git.tuffraid.net/cowch/lstV2/commits/b7773ec02aa26c01530723dc146e0c9f4dae41d3))
|
||||
|
||||
|
||||
### 📝 Chore
|
||||
|
||||
* **builds:** bummped lstv1 build ([739e6bb](https://git.tuffraid.net/cowch/lstV2/commits/739e6bbe9fa48fae682f9931a2c8bcb763feb636))
|
||||
* bump build number to 20 ([0975f4e](https://git.tuffraid.net/cowch/lstV2/commits/0975f4e499a8a2aec0662352cb4496299292d4ea))
|
||||
* bump build number to 21 ([4e885ce](https://git.tuffraid.net/cowch/lstV2/commits/4e885ce74c02ee1fd95a93402d1d32ecf357b1cb))
|
||||
* bump build number to 22 ([43ca16d](https://git.tuffraid.net/cowch/lstV2/commits/43ca16dc807b9fa3496498e90a871568f0e5f54c))
|
||||
* bump build number to 23 ([bff0e77](https://git.tuffraid.net/cowch/lstV2/commits/bff0e77766b49c72d77fa55cfaab2d347a1a75dc))
|
||||
* bump build number to 24 ([f4433f4](https://git.tuffraid.net/cowch/lstV2/commits/f4433f41926dab72a239fc585ddd1e14acb80c7e))
|
||||
* bump build number to 25 ([f1979f0](https://git.tuffraid.net/cowch/lstV2/commits/f1979f0fc914f3b94bad1816cd405a6431d1ba4a))
|
||||
* bump build number to 26 ([491de26](https://git.tuffraid.net/cowch/lstV2/commits/491de26a0bbe6dfca9e1cb068991789c29a09ac0))
|
||||
* bump build number to 27 ([26ea8d5](https://git.tuffraid.net/cowch/lstV2/commits/26ea8d5e89bb1b44ceb6b205da3ad158c603fca0))
|
||||
* bump build number to 28 ([1e02d4f](https://git.tuffraid.net/cowch/lstV2/commits/1e02d4fa4fc1a007634da49fe2697d63c89cba18))
|
||||
* bump build number to 29 ([9796947](https://git.tuffraid.net/cowch/lstV2/commits/9796947db5443c8b5678d7502037458d680e4018))
|
||||
* bump build number to 30 ([4a48dd2](https://git.tuffraid.net/cowch/lstV2/commits/4a48dd2bb57064953ef6e192c76b91bb844a24de))
|
||||
* bump build number to 31 ([227e2aa](https://git.tuffraid.net/cowch/lstV2/commits/227e2aa00c2a3e5526e0347c76676de345dfea5d))
|
||||
* bump build number to 32 ([f035e6f](https://git.tuffraid.net/cowch/lstV2/commits/f035e6f14a9e2123a10010d4f515f78f0c222599))
|
||||
* bump build number to 33 ([03aa7e5](https://git.tuffraid.net/cowch/lstV2/commits/03aa7e5aeee39b1b390b4da2019f44459487e0a0))
|
||||
* bump build number to 34 ([f4c44fb](https://git.tuffraid.net/cowch/lstV2/commits/f4c44fb02ba857890af66a2747381ef1c3def25b))
|
||||
* bump build number to 35 ([8b72a1b](https://git.tuffraid.net/cowch/lstV2/commits/8b72a1b47e715f4146e586e9fdd6abcabd97743d))
|
||||
* bump build number to 36 ([8a143fb](https://git.tuffraid.net/cowch/lstV2/commits/8a143fbb19d2270b0139a7a4edbce745c477b6b1))
|
||||
* bump build number to 37 ([b0634d9](https://git.tuffraid.net/cowch/lstV2/commits/b0634d9427f6fff3a239680204b5e6daf6163e37))
|
||||
* bump build number to 38 ([751b9d5](https://git.tuffraid.net/cowch/lstV2/commits/751b9d5701cf7e81641f154a02041869e1b80c49))
|
||||
* bump build number to 39 ([6dd5f4b](https://git.tuffraid.net/cowch/lstV2/commits/6dd5f4b61f3a27779980cf14915c4394256eae20))
|
||||
* bump build number to 40 ([d98a659](https://git.tuffraid.net/cowch/lstV2/commits/d98a6592628f36c654039ccea25db163ddf15e8c))
|
||||
* bump build number to 41 ([807a4ca](https://git.tuffraid.net/cowch/lstV2/commits/807a4ca6993b5dfa82a45aaf44a2d661c3e03428))
|
||||
* bump build number to 42 ([196ea00](https://git.tuffraid.net/cowch/lstV2/commits/196ea009720a65c520f5626658ac7885a41796cb))
|
||||
* bump build number to 43 ([34b80cf](https://git.tuffraid.net/cowch/lstV2/commits/34b80cf2368228b70cd6a79d44ba0f95b5c5941a))
|
||||
* bump build number to 44 ([808e3d8](https://git.tuffraid.net/cowch/lstV2/commits/808e3d84efa0889077a12cbf8e28df794a51f2bc))
|
||||
* bump build number to 45 ([38d1043](https://git.tuffraid.net/cowch/lstV2/commits/38d10436069d3db1612a622d9b9e0c7aec04f6dd))
|
||||
* bump build number to 46 ([532a722](https://git.tuffraid.net/cowch/lstV2/commits/532a7227631cf67664ca41133330d8cfa59f429b))
|
||||
* bump build number to 47 ([f665406](https://git.tuffraid.net/cowch/lstV2/commits/f6654067f5f01c3ee4855f0cbe07593f13263ce7))
|
||||
* bump build number to 48 ([97b9c4d](https://git.tuffraid.net/cowch/lstV2/commits/97b9c4db4a67faf68edae4dc1217b28f87d003e8))
|
||||
* bump build number to 49 ([acb9876](https://git.tuffraid.net/cowch/lstV2/commits/acb9876d9b715894481d31d9bb104e54561710d0))
|
||||
* **pkg updates:** updated all pkgs ([fb9ee15](https://git.tuffraid.net/cowch/lstV2/commits/fb9ee15bda89257815012023ed5543ef55b5f379))
|
||||
* **updatescript:** added in so we can do a full install with an env creation for old ([cb3ab66](https://git.tuffraid.net/cowch/lstV2/commits/cb3ab668d866ea9e3428e548d9065a9681174e82))
|
||||
|
||||
## [2.8.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.7.0...v2.8.0) (2025-03-16)
|
||||
|
||||
|
||||
### 📝 Chore
|
||||
|
||||
* bump build number to 19 ([3ea6dc5](https://git.tuffraid.net/cowch/lstV2/commits/3ea6dc5bc46b6a060548566ea606095d0e30c96c))
|
||||
|
||||
|
||||
### 📝 Testing Code
|
||||
|
||||
* **api:** testing api options to reduce the code as we keep repeating same functions ([7432dec](https://git.tuffraid.net/cowch/lstV2/commits/7432decd3ce9033ad310850fe5b0cbba076526a5))
|
||||
* **printers:** started printer migration ([04a607d](https://git.tuffraid.net/cowch/lstV2/commits/04a607d3bcf2020624e45ba5ddf83f0b13acbbc7))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **reader:** added in missing columns needed in the table ([020fdc8](https://git.tuffraid.net/cowch/lstV2/commits/020fdc83af2b5e90b76fa43b884965f82b5db466))
|
||||
* **rfid tags:** update tag table to include what is needed and changed columns to be correct ([d178e04](https://git.tuffraid.net/cowch/lstV2/commits/d178e0436247d83b3e33dc7c92bb49aa023b6198))
|
||||
* **rfid:** cleaned up contorller folder ([1a79a97](https://git.tuffraid.net/cowch/lstV2/commits/1a79a9792913bb984f230f29fede1bfa6da2991f))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **api:** added in a response function to reduce the over responses as they are always the same ([7bfb48b](https://git.tuffraid.net/cowch/lstV2/commits/7bfb48b81fcfe759e8f448db30db93b1c320de1a))
|
||||
* **rfid:** add/update readers now possible ([4aae659](https://git.tuffraid.net/cowch/lstV2/commits/4aae659ee41d952e823ba4582b4d3fab05bd2500))
|
||||
* **rfid:** no read console log for now but will show in frontend as well ([4b92a28](https://git.tuffraid.net/cowch/lstV2/commits/4b92a28dfa3db011a451aff913175ff8d21feb1b))
|
||||
* **rfid:** reader and tag db completed ([33803a6](https://git.tuffraid.net/cowch/lstV2/commits/33803a69a6b9b0d24297f7a9d463d16dbae4ca44))
|
||||
* **tag reading:** more tag reading updates, with more contorl now ([cb59f58](https://git.tuffraid.net/cowch/lstV2/commits/cb59f589264583b2ee6d8f0255d3ceb33f101a01))
|
||||
|
||||
## [2.7.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.6.0...v2.7.0) (2025-03-15)
|
||||
|
||||
|
||||
### 📝 Chore
|
||||
|
||||
* bump build number to 10 ([245ba19](https://git.tuffraid.net/cowch/lstV2/commits/245ba19cdc7e67fec9343bb9eba90287d8951854))
|
||||
* bump build number to 11 ([c9a5203](https://git.tuffraid.net/cowch/lstV2/commits/c9a520313160959ed929351f55bd18606b6500a9))
|
||||
* bump build number to 12 ([b1d25c7](https://git.tuffraid.net/cowch/lstV2/commits/b1d25c7ba27a72c42803a3b482234c41670a07a2))
|
||||
* bump build number to 13 ([bcd65b4](https://git.tuffraid.net/cowch/lstV2/commits/bcd65b4b91cf6cb24a32323ead5ff6a73d3c9d3d))
|
||||
* bump build number to 14 ([a2fb845](https://git.tuffraid.net/cowch/lstV2/commits/a2fb845e2efbeb8becf91082343770b2d730330c))
|
||||
* bump build number to 15 ([0b72ffa](https://git.tuffraid.net/cowch/lstV2/commits/0b72ffa935be81476a15166fd60ae8cabe29d297))
|
||||
* bump build number to 16 ([44da09d](https://git.tuffraid.net/cowch/lstV2/commits/44da09d22c4d946c981d408d797ea121d26690b6))
|
||||
* bump build number to 17 ([1ce5f9a](https://git.tuffraid.net/cowch/lstV2/commits/1ce5f9acf752f5f4bcb4b2ff1c71f54ffb9331f3))
|
||||
* bump build number to 18 ([b6f1cfd](https://git.tuffraid.net/cowch/lstV2/commits/b6f1cfdc6c71e351fb90ec7729e0d0f89322174f))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **frontend:** removed tanstack devTools ([3594278](https://git.tuffraid.net/cowch/lstV2/commits/359427824bae319b7f2f406eb52b8f9c43be198f))
|
||||
* **frontend:** removed the caption from settings table ([35acd2b](https://git.tuffraid.net/cowch/lstV2/commits/35acd2b0b3af7b9d010cb8f78f088ab3a539c54b))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **frontend:** added in proper links for settings and servers to the sidebar ([cbdd218](https://git.tuffraid.net/cowch/lstV2/commits/cbdd218fe454e38a7cf0c4d0ddf60d1f20e15ee2))
|
||||
* **frontend:** added in update server page only for sysAdmin ([625d596](https://git.tuffraid.net/cowch/lstV2/commits/625d5969be2dbde9e97f6607c33c5e5b14e3d192))
|
||||
* **new setting:** added in devDir ment for updating servers ([5b97d07](https://git.tuffraid.net/cowch/lstV2/commits/5b97d078c583f3e78d52dbc135da99a0175c8e54))
|
||||
* **server upgrade:** added in a catch incase we try to upgrade again ([ab16059](https://git.tuffraid.net/cowch/lstV2/commits/ab16059387b7cb46e2a3d86f6da09a31899bd5d6))
|
||||
* **server:** added in update server as well as get serverdata ([df252e7](https://git.tuffraid.net/cowch/lstV2/commits/df252e72b39d811ffbbb0af49aaf43ff22081f48))
|
||||
* **serverdata:** added catch if we are not on localhost we cant actaully see the devDir in set ([f3fa617](https://git.tuffraid.net/cowch/lstV2/commits/f3fa617aa53ed35f461b71f5e479ea521412936e))
|
||||
* **serverdata:** added in bowling green 2 ([7529cc5](https://git.tuffraid.net/cowch/lstV2/commits/7529cc5b0cc2c6542e8f4af07d465b6a9f4295b1))
|
||||
* **sql query:** added 2 catches if not connected dont run ([cb7a406](https://git.tuffraid.net/cowch/lstV2/commits/cb7a4068fcffc7a1d85c2e04f2eeaebdc264705c))
|
||||
* **sql server:** added in the ping check to not spam if we are not connected ([e4d15ef](https://git.tuffraid.net/cowch/lstV2/commits/e4d15ef051f6c72d2cca9bb9fb61ff78849db8c4))
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **frontend:** if the modules returns and error we want to use an empty array ([2370d45](https://git.tuffraid.net/cowch/lstV2/commits/2370d45220c5e1c3215a3f2fce9582d8f2bbd3ed))
|
||||
* **rfid:** correction to the params and incorrect naming ([a73c63c](https://git.tuffraid.net/cowch/lstV2/commits/a73c63cefa67f43300f4695f40f2248bcab8f40e))
|
||||
* **zippaths:** corrected the paths to the src that were moved the the env ([246b5a1](https://git.tuffraid.net/cowch/lstV2/commits/246b5a17bd4ffe47654416fa4a1be446f8bffbd0))
|
||||
|
||||
## [2.6.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.5.1...v2.6.0) (2025-03-14)
|
||||
|
||||
|
||||
### 📝 Chore
|
||||
|
||||
* bump build number to 9 ([542c08f](https://git.tuffraid.net/cowch/lstV2/commits/542c08f10ba590e7174864c7970fd0ac03ffebf9))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **pkg:** removed dir stuff and moved to env ([242494a](https://git.tuffraid.net/cowch/lstV2/commits/242494a4eaaca0aaf12e9a309e5611e17ac9ccb0))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **app update:** finished the app build / deploy functions and a scripts migration ([81eb10e](https://git.tuffraid.net/cowch/lstV2/commits/81eb10e021fd29e33ee3f7393439c72427df9561))
|
||||
* **pkgdata:** moved the pkg info to a global function ([51b81b9](https://git.tuffraid.net/cowch/lstV2/commits/51b81b97bc4c4b112b128818d203deba49fb5011))
|
||||
|
||||
### [2.5.1](https://git.tuffraid.net/cowch/lstV2/compare/v2.5.0...v2.5.1) (2025-03-14)
|
||||
|
||||
|
||||
### 📝 Testing Code
|
||||
|
||||
* **lst:** readded prodBuild ([5696871](https://git.tuffraid.net/cowch/lstV2/commits/5696871b8d56374b508df77e3c6b18421865605f))
|
||||
|
||||
## [2.5.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.4.0...v2.5.0) (2025-03-14)
|
||||
|
||||
|
||||
### 🚀 Performance
|
||||
|
||||
* **auth:** changed the way logs look in the setRoles ([d95b81d](https://git.tuffraid.net/cowch/lstV2/commits/d95b81d303192f176a970ee1de5ffd7afd5d475c))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **auth:** added in timeout for adding new roles to make sure everything else is connected 1st ([2130ade](https://git.tuffraid.net/cowch/lstV2/commits/2130adedb3a766268a73950cd21ac3a21e20bdeb))
|
||||
* **sql:** reduced the time before start up ([80567d2](https://git.tuffraid.net/cowch/lstV2/commits/80567d2dd95fa141fea21fbaf8f9bc22386f92d0))
|
||||
* **system:** checks now in time out to ensure everything connected ([78dc597](https://git.tuffraid.net/cowch/lstV2/commits/78dc597477a51a8b86f7b3dde1e02b9cdb732afa))
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **auth:** proper logging for errors in role check ([90d0760](https://git.tuffraid.net/cowch/lstV2/commits/90d0760352bf9d77dd2c1c3c449f1e7f4a5a4056))
|
||||
* **consumption:** correction to the status code to always be 200 on success: true ([5c2d79c](https://git.tuffraid.net/cowch/lstV2/commits/5c2d79c125d051e44cf4e5aa8316127d1619af3e))
|
||||
* **crash alerts:** remmoved close sql as it spammed bad when we couldnt connect ([84ce009](https://git.tuffraid.net/cowch/lstV2/commits/84ce009310b2ddd5daf5af25a38ca78c7469ebff))
|
||||
* **query:** shipmentPallets change from last move to production date ([155b7d9](https://git.tuffraid.net/cowch/lstV2/commits/155b7d92aa876cf9e313dc4cd2cd68193f4a9772))
|
||||
* **serverdata:** fixed a bug in the serverData check ([4b53700](https://git.tuffraid.net/cowch/lstV2/commits/4b53700603ab2110f5abd2dbc9354a99e5950971))
|
||||
* **sql server:** correct return when we cant connect to the server ([d5e9176](https://git.tuffraid.net/cowch/lstV2/commits/d5e9176d53b396a9dc16686ad2cfa8b59e4672eb))
|
||||
* **sql server:** reduced the risk of error when missing data ([6158f25](https://git.tuffraid.net/cowch/lstV2/commits/6158f254b7de890a2262a59d0aeb32411d17d6fd))
|
||||
* **sql:** oops forgot to remove the returns i put in to ignore an error ([bb1635a](https://git.tuffraid.net/cowch/lstV2/commits/bb1635a5b4dc0e6ca324bdf7ea8ecd89c33a9d3d))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **build and zip:** migrated build and zip process from v1 ([df85a30](https://git.tuffraid.net/cowch/lstV2/commits/df85a30dffdc87e5c0475e45559ec81921605d60))
|
||||
* **db:** serverData added in ([3c2b10b](https://git.tuffraid.net/cowch/lstV2/commits/3c2b10b28cdafd269c0f24dcb1dfa79471eb52d0))
|
||||
* **install:** added install instructions ([f69a40a](https://git.tuffraid.net/cowch/lstV2/commits/f69a40af3af932c0e4e8552b17b099edd64b280a))
|
||||
* **ocme:** added in shipment data with increased checked ([c386c5e](https://git.tuffraid.net/cowch/lstV2/commits/c386c5ea8b5500a99c49c2750d9b1f3e2816521a))
|
||||
* **ping:** added in 3 new funcitons to ping devices to see if they are alive before doing something ([a18cf65](https://git.tuffraid.net/cowch/lstV2/commits/a18cf652fad80c3c5b22e3ec50999831930c211b))
|
||||
* **rfid:** added tables to the db for tags and readers ([dc04d97](https://git.tuffraid.net/cowch/lstV2/commits/dc04d97229f61bfeab59d0cf27135d9e0efada8a))
|
||||
* **server:** added a missing db prevention crash ([b9724aa](https://git.tuffraid.net/cowch/lstV2/commits/b9724aa89c47edaf1dbbf730e0605166626c9936))
|
||||
* **serverdata:** all server info will be in all servers now for reduncay ([58975ca](https://git.tuffraid.net/cowch/lstV2/commits/58975ca117158e9f017343f938b8943694433ed3))
|
||||
* **settings:** added in more settings ([d6942dd](https://git.tuffraid.net/cowch/lstV2/commits/d6942dd9826cfa4497a60a67eeb67d0957a904ed))
|
||||
* **sql server:** added ping check to make sure the server is online before we try to connect ([c2c43b1](https://git.tuffraid.net/cowch/lstV2/commits/c2c43b1e2248bb2c48599f8f83f29568dc102a4d))
|
||||
|
||||
|
||||
### 📝 Chore
|
||||
|
||||
* bump build number to 4 ([bd4ca7b](https://git.tuffraid.net/cowch/lstV2/commits/bd4ca7ba63a9dafd1b96dd1672ef221da558a7c2))
|
||||
* bump build number to 5 ([d852e9e](https://git.tuffraid.net/cowch/lstV2/commits/d852e9e57370a9f77664eb74999c87b494b56c4e))
|
||||
* bump build number to 6 ([8363cc2](https://git.tuffraid.net/cowch/lstV2/commits/8363cc2816b64f60d66926bb006c2079b2968cfb))
|
||||
* bump build number to 7 ([571b74f](https://git.tuffraid.net/cowch/lstV2/commits/571b74f0e7a5f1e6ef1a13548c21c6f8c7d0c770))
|
||||
* bump build number to 8 ([5f5757c](https://git.tuffraid.net/cowch/lstV2/commits/5f5757c7848903abfce50e2992ed31f3ecfb99ac))
|
||||
* **ignore:** ignoring build folder as we dont want this to push to the server ([eaac814](https://git.tuffraid.net/cowch/lstV2/commits/eaac8143e9fe0e93f59e6229b39a8aa6bc49d79e))
|
||||
* **pkg:** added admin section as well as correct a couple areas for builds ([45b1f7c](https://git.tuffraid.net/cowch/lstV2/commits/45b1f7c0a6a529fdde9624b257021ea30997ffdd))
|
||||
|
||||
|
||||
### 📈 Project changes
|
||||
|
||||
* **lst:** changes to improve the build process ([200ec97](https://git.tuffraid.net/cowch/lstV2/commits/200ec975f742de8a4c7d28bdc1362dba9e15a582))
|
||||
|
||||
|
||||
### 📝 Testing Code
|
||||
|
||||
* **apihits:** monitoring this still to make sure its working as intended but low priority ([9c66e6a](https://git.tuffraid.net/cowch/lstV2/commits/9c66e6aac6200c34afa74d8c4d178374dd5ef5ca))
|
||||
* **buildserver:** build serer checks ([dd88f25](https://git.tuffraid.net/cowch/lstV2/commits/dd88f258edf4d7c0cefd8d6baa290c7cf2b9b805))
|
||||
* **pkg:** removed the combined command to deploy ([1cb8b70](https://git.tuffraid.net/cowch/lstV2/commits/1cb8b7043008b1e396b0fe43175459284ce35561))
|
||||
* **rfid:** intial trials built ([da04e9d](https://git.tuffraid.net/cowch/lstV2/commits/da04e9d35d2aecbc9c4a930fe9a217409c35aef2))
|
||||
* **updates:** rewrite of update system ([0054c8f](https://git.tuffraid.net/cowch/lstV2/commits/0054c8f7d44745bdf710696764ff909bed6f3647))
|
||||
|
||||
## [2.4.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.3.0...v2.4.0) (2025-03-10)
|
||||
|
||||
|
||||
|
||||
1
database/migrations/0016_wet_doctor_strange.sql
Normal file
1
database/migrations/0016_wet_doctor_strange.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "serverData" ADD COLUMN "isUpgrading" boolean DEFAULT false;
|
||||
1
database/migrations/0017_bitter_brood.sql
Normal file
1
database/migrations/0017_bitter_brood.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "rfidReaders" ADD COLUMN "lastTrigger" timestamp DEFAULT now();
|
||||
1
database/migrations/0018_lovely_landau.sql
Normal file
1
database/migrations/0018_lovely_landau.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "rfidReaders" ADD COLUMN "active" boolean DEFAULT true;
|
||||
2
database/migrations/0019_greedy_justice.sql
Normal file
2
database/migrations/0019_greedy_justice.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "rfidTags" ADD COLUMN "antenna" numeric;--> statement-breakpoint
|
||||
ALTER TABLE "rfidTags" ADD COLUMN "tagStrength" numeric;
|
||||
2
database/migrations/0020_empty_thundra.sql
Normal file
2
database/migrations/0020_empty_thundra.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "rfidTags" ALTER COLUMN "counts" SET DEFAULT '[]'::jsonb;--> statement-breakpoint
|
||||
ALTER TABLE "rfidTags" ALTER COLUMN "counts" DROP NOT NULL;
|
||||
4
database/migrations/0021_premium_albert_cleary.sql
Normal file
4
database/migrations/0021_premium_albert_cleary.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE "rfidTags" ALTER COLUMN "runningNumber" SET DATA TYPE integer;--> statement-breakpoint
|
||||
ALTER TABLE "rfidTags" ALTER COLUMN "runningNumber" DROP NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "rfidTags" ALTER COLUMN "antenna" SET DATA TYPE integer;--> statement-breakpoint
|
||||
ALTER TABLE "rfidTags" ALTER COLUMN "tagStrength" SET DATA TYPE integer;
|
||||
1
database/migrations/0022_amused_true_believers.sql
Normal file
1
database/migrations/0022_amused_true_believers.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "rfidReaders" ADD COLUMN "lastTiggerGood" boolean DEFAULT true;
|
||||
2
database/migrations/0023_wealthy_marvel_boy.sql
Normal file
2
database/migrations/0023_wealthy_marvel_boy.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "logs" ADD COLUMN "checked" boolean DEFAULT false;--> statement-breakpoint
|
||||
ALTER TABLE "logs" ADD COLUMN "checkedAt" timestamp;
|
||||
9
database/migrations/0024_curved_venom.sql
Normal file
9
database/migrations/0024_curved_venom.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE "manualPrinting" (
|
||||
"print_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"line" integer,
|
||||
"printReason" text NOT NULL,
|
||||
"initials" text NOT NULL,
|
||||
"additionalComments" text NOT NULL,
|
||||
"add_date" timestamp DEFAULT now(),
|
||||
"add_user" text
|
||||
);
|
||||
2
database/migrations/0025_amusing_sugar_man.sql
Normal file
2
database/migrations/0025_amusing_sugar_man.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
DROP TABLE "apiHits" CASCADE;--> statement-breakpoint
|
||||
DROP TABLE "eom" CASCADE;
|
||||
1040
database/migrations/meta/0016_snapshot.json
Normal file
1040
database/migrations/meta/0016_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1047
database/migrations/meta/0017_snapshot.json
Normal file
1047
database/migrations/meta/0017_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1054
database/migrations/meta/0018_snapshot.json
Normal file
1054
database/migrations/meta/0018_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1066
database/migrations/meta/0019_snapshot.json
Normal file
1066
database/migrations/meta/0019_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1067
database/migrations/meta/0020_snapshot.json
Normal file
1067
database/migrations/meta/0020_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1067
database/migrations/meta/0021_snapshot.json
Normal file
1067
database/migrations/meta/0021_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1074
database/migrations/meta/0022_snapshot.json
Normal file
1074
database/migrations/meta/0022_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1087
database/migrations/meta/0023_snapshot.json
Normal file
1087
database/migrations/meta/0023_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1144
database/migrations/meta/0024_snapshot.json
Normal file
1144
database/migrations/meta/0024_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1056
database/migrations/meta/0025_snapshot.json
Normal file
1056
database/migrations/meta/0025_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -113,6 +113,76 @@
|
||||
"when": 1741952150913,
|
||||
"tag": "0015_wonderful_lady_vermin",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "7",
|
||||
"when": 1742051878587,
|
||||
"tag": "0016_wet_doctor_strange",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"version": "7",
|
||||
"when": 1742133439717,
|
||||
"tag": "0017_bitter_brood",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"version": "7",
|
||||
"when": 1742133828291,
|
||||
"tag": "0018_lovely_landau",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"version": "7",
|
||||
"when": 1742139737945,
|
||||
"tag": "0019_greedy_justice",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"version": "7",
|
||||
"when": 1742143798267,
|
||||
"tag": "0020_empty_thundra",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"version": "7",
|
||||
"when": 1742144973347,
|
||||
"tag": "0021_premium_albert_cleary",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"version": "7",
|
||||
"when": 1742156466912,
|
||||
"tag": "0022_amused_true_believers",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"version": "7",
|
||||
"when": 1742346003832,
|
||||
"tag": "0023_wealthy_marvel_boy",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"version": "7",
|
||||
"when": 1742408812383,
|
||||
"tag": "0024_curved_venom",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 25,
|
||||
"version": "7",
|
||||
"when": 1742655504936,
|
||||
"tag": "0025_amusing_sugar_man",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,6 +10,8 @@ export const logs = pgTable(
|
||||
username: text("username").default("LST_Serivce"),
|
||||
service: text("service").notNull().default("system"),
|
||||
message: text("message").notNull(),
|
||||
checked: boolean("checked").default(false),
|
||||
checkedAt: timestamp("checkedAt"),
|
||||
created_at: timestamp("add_Date").defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
|
||||
20
database/schema/ocpManualPrint.ts
Normal file
20
database/schema/ocpManualPrint.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {text, pgTable, timestamp, uuid, integer} from "drizzle-orm/pg-core";
|
||||
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
|
||||
export const manualPrinting = pgTable("manualPrinting", {
|
||||
print_id: uuid("print_id").defaultRandom().primaryKey(),
|
||||
line: integer("line"),
|
||||
printReason: text("printReason").notNull(),
|
||||
initials: text("initials").notNull(),
|
||||
additionalComments: text("additionalComments").notNull(),
|
||||
add_date: timestamp("add_date").defaultNow(),
|
||||
add_user: text("add_user"),
|
||||
});
|
||||
|
||||
// Schema for inserting a user - can be used to validate API requests
|
||||
// export const insertRolesSchema = createInsertSchema(roles, {
|
||||
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
|
||||
// });
|
||||
// Schema for selecting a Expenses - can be used to validate API responses
|
||||
export const selectRolesSchema = createSelectSchema(manualPrinting);
|
||||
@@ -9,6 +9,9 @@ export const rfidReaders = pgTable(
|
||||
reader: text("reader"),
|
||||
readerIP: text("readerIP"),
|
||||
lastHeartBeat: timestamp("lastHeartBeat").defaultNow(),
|
||||
lastTrigger: timestamp("lastTrigger").defaultNow(),
|
||||
lastTriggerGood: boolean("lastTiggerGood").default(true),
|
||||
active: boolean("active").default(true),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex, jsonb} from "drizzle-orm/pg-core";
|
||||
import {text, pgTable, timestamp, uuid, uniqueIndex, jsonb, integer} from "drizzle-orm/pg-core";
|
||||
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
|
||||
@@ -9,9 +9,11 @@ export const rfidTags = pgTable(
|
||||
tagHex: text("tagHex"),
|
||||
tag: text("tag"),
|
||||
lastRead: timestamp("timeStamp").defaultNow(),
|
||||
counts: jsonb("counts").notNull(), //.default([{area: 1, timesHere: 5}]).notNull(),
|
||||
counts: jsonb("counts").default([]), // example [{area: Line3.2, count: 1}, {area: line3.1, count: 6}]
|
||||
lastareaIn: text("lastareaIn").notNull(),
|
||||
runningNumber: numeric("runningNumber").notNull(),
|
||||
runningNumber: integer("runningNumber"),
|
||||
antenna: integer("antenna"),
|
||||
tagStrength: integer("tagStrength"),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
@@ -21,8 +23,8 @@ export const rfidTags = pgTable(
|
||||
);
|
||||
|
||||
// Schema for inserting a user - can be used to validate API requests
|
||||
// export const insertRolesSchema = createInsertSchema(roles, {
|
||||
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
|
||||
// });
|
||||
export const insertRolesSchema = createInsertSchema(rfidTags, {
|
||||
tagHex: z.string().min(3, {message: "Tag Should have more than 3 characters"}),
|
||||
});
|
||||
// Schema for selecting a Expenses - can be used to validate API responses
|
||||
export const selectRolesSchema = createSelectSchema(rfidTags);
|
||||
|
||||
@@ -24,6 +24,7 @@ export const serverData = pgTable(
|
||||
shippingHours: text("shippingHours").default('[{"early": "06:30", "late": "23:00"}]'),
|
||||
tiPostTime: text("tiPostTime").default('[{"from": "24", "to": "24"}]'),
|
||||
otherSettings: jsonb("otherSettings").default([{specialInstructions: "something for ti", active: false}]),
|
||||
isUpgrading: boolean("isUpgrading").default(false),
|
||||
},
|
||||
|
||||
(table) => [
|
||||
|
||||
842
frontend/package-lock.json
generated
842
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,10 +8,11 @@
|
||||
"build": "rimraf dist && tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"shad": "npx shadcn@canary add "
|
||||
"shad": "npx shadcn@canary add ",
|
||||
"checkupdates": "npm-check-updates"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^4.1.2",
|
||||
"@hookform/resolvers": "^4.1.3",
|
||||
"@radix-ui/react-avatar": "^1.1.3",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.3",
|
||||
@@ -19,49 +20,52 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||
"@radix-ui/react-label": "^2.1.2",
|
||||
"@radix-ui/react-popover": "^1.1.6",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-separator": "^1.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@tailwindcss/vite": "^4.0.9",
|
||||
"@tanstack/react-query": "^5.66.9",
|
||||
"@tanstack/react-router": "^1.111.11",
|
||||
"@tailwindcss/vite": "^4.0.15",
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@tanstack/react-router": "^1.114.27",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"hono": "^4.7.2",
|
||||
"hono": "^4.7.5",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lucide-react": "^0.476.0",
|
||||
"next-themes": "^0.4.4",
|
||||
"lucide-react": "^0.483.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"npm-check-updates": "^17.1.16",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-grid-layout": "^1.5.0",
|
||||
"react-grid-layout": "^1.5.1",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"sonner": "^2.0.1",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwindcss": "^4.0.9",
|
||||
"tailwindcss": "^4.0.15",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.24.2",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@tanstack/router-devtools": "^1.106.0",
|
||||
"@tanstack/router-plugin": "^1.106.0",
|
||||
"@types/react": "^19.0.10",
|
||||
"@eslint/js": "^9.23.0",
|
||||
"@tanstack/router-devtools": "^1.114.27",
|
||||
"@tanstack/router-plugin": "^1.114.27",
|
||||
"@types/react": "^19.0.12",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@types/react-grid-layout": "^1.3.5",
|
||||
"@vitejs/plugin-react-swc": "^3.8.0",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"@vitejs/plugin-react-swc": "^3.8.1",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^15.15.0",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.2.0"
|
||||
"globals": "^16.0.0",
|
||||
"typescript": "~5.8.2",
|
||||
"typescript-eslint": "^8.27.0",
|
||||
"vite": "^6.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
22
frontend/src/components/admin/servers/RestartServer.tsx
Normal file
22
frontend/src/components/admin/servers/RestartServer.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
||||
import {RotateCcw} from "lucide-react";
|
||||
|
||||
export default function RestartServer() {
|
||||
return (
|
||||
<div>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant={"outline"} size={"icon"}>
|
||||
<RotateCcw />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Restart Server ... Needs added still</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
125
frontend/src/components/admin/servers/ServerPage.tsx
Normal file
125
frontend/src/components/admin/servers/ServerPage.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { useSessionStore } from "@/lib/store/sessionStore";
|
||||
import { useModuleStore } from "@/lib/store/useModuleStore";
|
||||
import { getServers } from "@/utils/querys/servers";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useRouter } from "@tanstack/react-router";
|
||||
import { format } from "date-fns";
|
||||
import UpdateServer from "./UpdateServer";
|
||||
import { adminUrlCheck } from "@/utils/adminUrlCheck";
|
||||
import RestartServer from "./RestartServer";
|
||||
import StopServer from "./StopServer";
|
||||
import StartServer from "./StartServer";
|
||||
|
||||
export type Servers = {
|
||||
server_id?: string;
|
||||
sName?: string;
|
||||
serverDNS?: string;
|
||||
plantToken?: string;
|
||||
idAddress: string;
|
||||
lastUpdated: string;
|
||||
isUpgrading: boolean;
|
||||
};
|
||||
|
||||
export default function ServerPage() {
|
||||
const { user, token } = useSessionStore();
|
||||
const { modules } = useModuleStore();
|
||||
const router = useRouter();
|
||||
|
||||
const { data, isError, error, isLoading } = useQuery(getServers(token ?? ""));
|
||||
|
||||
const adminModule = modules.filter((n) => n.name === "admin");
|
||||
const userLevel =
|
||||
user?.roles?.filter((r) => r.module_id === adminModule[0].module_id) || [];
|
||||
|
||||
if (!adminModule[0]?.roles?.includes(userLevel[0]?.role)) {
|
||||
router.navigate({ to: "/" });
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return <div>{JSON.stringify(error)}</div>;
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
return (
|
||||
<LstCard className="m-2 flex place-content-center w-dvh">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Server</TableHead>
|
||||
<TableHead>PlantToken</TableHead>
|
||||
<TableHead>IP Address</TableHead>
|
||||
<TableHead>Date Last updated</TableHead>
|
||||
<TableHead>Update Server</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<TableBody>
|
||||
{Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<TableBody>
|
||||
{data?.map((server: Servers) => {
|
||||
const strippedDate = server.lastUpdated.replace("Z", ""); // Remove Z
|
||||
const formattedDate = format(strippedDate, "MM/dd/yyyy hh:mm a");
|
||||
return (
|
||||
<TableRow key={server.server_id}>
|
||||
<TableCell className="font-medium">{server.sName}</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{server.serverDNS}
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{server.plantToken}
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{server.idAddress}
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">{formattedDate}</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{adminUrlCheck() && (
|
||||
<div className="flex flex-row">
|
||||
<UpdateServer server={server} token={token as string} />
|
||||
<StartServer />
|
||||
<StopServer />
|
||||
<RestartServer />
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
22
frontend/src/components/admin/servers/StartServer.tsx
Normal file
22
frontend/src/components/admin/servers/StartServer.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
||||
import {Play} from "lucide-react";
|
||||
|
||||
export default function StartServer() {
|
||||
return (
|
||||
<div>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant={"outline"} size={"icon"}>
|
||||
<Play />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Start Server ... Needs added still</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
frontend/src/components/admin/servers/StopServer.tsx
Normal file
22
frontend/src/components/admin/servers/StopServer.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
||||
import {Octagon} from "lucide-react";
|
||||
|
||||
export default function StopServer() {
|
||||
return (
|
||||
<div>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant={"outline"} size={"icon"}>
|
||||
<Octagon />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Stop Server ... Needs added still</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
51
frontend/src/components/admin/servers/UpdateServer.tsx
Normal file
51
frontend/src/components/admin/servers/UpdateServer.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {CircleFadingArrowUp} from "lucide-react";
|
||||
import {toast} from "sonner";
|
||||
import {Servers} from "./ServerPage";
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import {getSettings} from "@/utils/querys/settings";
|
||||
import axios from "axios";
|
||||
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
||||
|
||||
export default function UpdateServer({server, token}: {server: Servers; token: string}) {
|
||||
const {data} = useQuery(getSettings(token ?? ""));
|
||||
const upgrade = async () => {
|
||||
let devDir = data.filter((n: any) => n.name === "devDir");
|
||||
toast.success("Server being upgraded in the background please wait.");
|
||||
try {
|
||||
const result = await axios.post(
|
||||
`/api/server/update/${server.plantToken}`,
|
||||
{devDir: devDir[0].value},
|
||||
{
|
||||
headers: {Authorization: `Bearer ${token}`},
|
||||
}
|
||||
);
|
||||
|
||||
if (result.data.success) {
|
||||
toast.success(result.data.message);
|
||||
}
|
||||
|
||||
if (!result.data.success) {
|
||||
toast.success(result.data.message);
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(`There was an error updating the server: ${error.data.message}`);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant={"outline"} size={"icon"} onClick={upgrade} disabled={server.isUpgrading}>
|
||||
<CircleFadingArrowUp />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Update {server.sName}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {LstCard} from "@/components/extendedUI/LstCard";
|
||||
import {Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import {useModuleStore} from "@/lib/store/useModuleStore";
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
@@ -37,9 +37,8 @@ export default function SettingsPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<LstCard className="m-2 flex place-content-center w-dvh">
|
||||
<LstCard className="m-2 flex place-content-center w-fit">
|
||||
<Table>
|
||||
<TableCaption>All Settings</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
|
||||
@@ -1,143 +1,164 @@
|
||||
import {useSessionStore} from "../../lib/store/sessionStore";
|
||||
import {LstCard} from "../extendedUI/LstCard";
|
||||
import {CardHeader} from "../ui/card";
|
||||
import {toast} from "sonner";
|
||||
import {z} from "zod";
|
||||
import {useRouter} from "@tanstack/react-router";
|
||||
import {Controller, useForm} from "react-hook-form";
|
||||
import {zodResolver} from "@hookform/resolvers/zod";
|
||||
import {Label} from "../ui/label";
|
||||
import {Input} from "../ui/input";
|
||||
import {Checkbox} from "../ui/checkbox";
|
||||
import {Button} from "../ui/button";
|
||||
import { useSessionStore } from "../../lib/store/sessionStore";
|
||||
import { LstCard } from "../extendedUI/LstCard";
|
||||
import { CardHeader } from "../ui/card";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import { useRouter } from "@tanstack/react-router";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Label } from "../ui/label";
|
||||
import { Input } from "../ui/input";
|
||||
import { Checkbox } from "../ui/checkbox";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
const FormSchema = z.object({
|
||||
username: z.string().min(1, "You must enter a valid username"),
|
||||
password: z.string().min(4, "You must enter a valid password"),
|
||||
rememberMe: z.boolean(),
|
||||
username: z.string().min(1, "You must enter a valid username"),
|
||||
password: z.string().min(4, "You must enter a valid password"),
|
||||
rememberMe: z.boolean(),
|
||||
});
|
||||
|
||||
const LoginForm = () => {
|
||||
const {setSession} = useSessionStore();
|
||||
const rememeberMe = localStorage.getItem("rememberMe") === "true";
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const router = useRouter();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: {errors},
|
||||
} = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
username: username || "",
|
||||
password: "",
|
||||
rememberMe: rememeberMe,
|
||||
const { setSession } = useSessionStore();
|
||||
const rememeberMe = localStorage.getItem("rememberMe") === "true";
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const router = useRouter();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
username: username || "",
|
||||
password: "",
|
||||
rememberMe: rememeberMe,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmitLogin = async (value: z.infer<typeof FormSchema>) => {
|
||||
// Do something with form data
|
||||
|
||||
// first update the rememberMe incase it was selected
|
||||
if (value.rememberMe) {
|
||||
localStorage.setItem("rememberMe", value.rememberMe.toString());
|
||||
localStorage.setItem("username", value.username);
|
||||
} else {
|
||||
localStorage.removeItem("rememberMe");
|
||||
localStorage.removeItem("username");
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
body: JSON.stringify({
|
||||
username: value.username,
|
||||
password: value.password,
|
||||
}),
|
||||
});
|
||||
|
||||
const onSubmitLogin = async (value: z.infer<typeof FormSchema>) => {
|
||||
// Do something with form data
|
||||
const data = await response.json();
|
||||
|
||||
// first update the rememberMe incase it was selected
|
||||
if (value.rememberMe) {
|
||||
localStorage.setItem("rememberMe", value.rememberMe.toString());
|
||||
localStorage.setItem("username", value.username);
|
||||
} else {
|
||||
localStorage.removeItem("rememberMe");
|
||||
localStorage.removeItem("username");
|
||||
}
|
||||
// Store token in localStorage
|
||||
// localStorage.setItem("auth_token", data.data.token);
|
||||
if (data.success) {
|
||||
const prod = btoa(`${value.username.toLowerCase()}:${value.password}`);
|
||||
const prodUser = { ...data.user, prod: prod };
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({username: value.username, password: value.password}),
|
||||
});
|
||||
setSession(prodUser, data.token);
|
||||
toast.success(`You are logged in as ${data.user.username}`);
|
||||
router.navigate({ to: "/" });
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!data.success) {
|
||||
toast.error(`${data.message}`);
|
||||
}
|
||||
|
||||
// Store token in localStorage
|
||||
// localStorage.setItem("auth_token", data.data.token);
|
||||
//console.log(data);
|
||||
} catch (err) {
|
||||
toast.error("Invalid credentials");
|
||||
}
|
||||
};
|
||||
|
||||
setSession(data.user, data.token);
|
||||
toast.success(`You are logged in as ${data.user.username}`);
|
||||
router.navigate({to: "/"});
|
||||
} catch (err) {
|
||||
toast.error("Invalid credentials");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="ml-[25%]">
|
||||
<LstCard className="p-3 w-96">
|
||||
<CardHeader>
|
||||
<div>
|
||||
<p className="text-2xl">Login to LST</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<hr className="rounded"></hr>
|
||||
<form onSubmit={handleSubmit(onSubmitLogin)}>
|
||||
<div>
|
||||
<Label htmlFor="username" className="m-1">
|
||||
Username
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="smith001"
|
||||
{...register("username")}
|
||||
className={errors.username ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.username}
|
||||
/>
|
||||
{errors.username && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.username.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<>
|
||||
<Label htmlFor={"password"} className="m-1">
|
||||
Password
|
||||
</Label>
|
||||
<Input
|
||||
type="password"
|
||||
{...register("password")}
|
||||
className={errors.password ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.password}
|
||||
/>
|
||||
{errors.password && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.password.message}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
<div className="flex justify-between pt-2">
|
||||
<div className="flex">
|
||||
<Controller
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Checkbox
|
||||
id="remember"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
<label
|
||||
htmlFor="remember"
|
||||
className="pl-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
remember me
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
control={control}
|
||||
name="rememberMe"
|
||||
defaultValue={rememeberMe}
|
||||
/>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div className="ml-[25%]">
|
||||
<LstCard className="p-3 w-96">
|
||||
<CardHeader>
|
||||
<div>
|
||||
<p className="text-2xl">Login to LST</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<hr className="rounded"></hr>
|
||||
<form onSubmit={handleSubmit(onSubmitLogin)}>
|
||||
<div>
|
||||
<Label htmlFor="username" className="m-1">
|
||||
Username
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="smith001"
|
||||
{...register("username")}
|
||||
className={errors.username ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.username}
|
||||
/>
|
||||
{errors.username && <p className="text-red-500 text-sm mt-1">{errors.username.message}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<>
|
||||
<Label htmlFor={"password"} className="m-1">
|
||||
Password
|
||||
</Label>
|
||||
<Input
|
||||
type="password"
|
||||
{...register("password")}
|
||||
className={errors.password ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.password}
|
||||
/>
|
||||
{errors.password && <p className="text-red-500 text-sm mt-1">{errors.password.message}</p>}
|
||||
</>
|
||||
</div>
|
||||
<div className="flex justify-between pt-2">
|
||||
<div className="flex">
|
||||
<Controller
|
||||
render={({field}) => (
|
||||
<>
|
||||
<Checkbox
|
||||
id="remember"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
<label
|
||||
htmlFor="remember"
|
||||
className="pl-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
remember me
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
control={control}
|
||||
name="rememberMe"
|
||||
defaultValue={rememeberMe}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</LstCard>
|
||||
</div>
|
||||
);
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</LstCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginForm;
|
||||
|
||||
@@ -15,7 +15,7 @@ import {Collapsible, CollapsibleContent, CollapsibleTrigger} from "../../ui/coll
|
||||
const items = [
|
||||
{
|
||||
title: "Servers",
|
||||
url: "#",
|
||||
url: "/servers",
|
||||
icon: Server,
|
||||
isActive: false,
|
||||
},
|
||||
@@ -31,13 +31,19 @@ const data = {
|
||||
title: "Settings",
|
||||
url: "/settings",
|
||||
icon: Settings,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
title: "Modules",
|
||||
url: "/modules",
|
||||
icon: Settings,
|
||||
isActive: false,
|
||||
},
|
||||
{
|
||||
title: "Swagger",
|
||||
url: "#",
|
||||
icon: Webhook,
|
||||
isActive: false,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
title: "Logs",
|
||||
@@ -91,12 +97,17 @@ export function AdminSideBar() {
|
||||
<SidebarMenuSub>
|
||||
{item.items.map((item) => (
|
||||
<SidebarMenuSubItem key={item.title}>
|
||||
<SidebarMenuSubButton asChild isActive={item.isActive}>
|
||||
<a href={item.url} target={item.newWindow ? "_blank" : "_self"}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</SidebarMenuSubButton>
|
||||
{item.isActive && (
|
||||
<SidebarMenuSubButton asChild>
|
||||
<a
|
||||
href={item.url}
|
||||
target={item.newWindow ? "_blank" : "_self"}
|
||||
>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</SidebarMenuSubButton>
|
||||
)}
|
||||
</SidebarMenuSubItem>
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
|
||||
@@ -51,6 +51,14 @@ const items = [
|
||||
module: "logistics",
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
title: "Ocme Cyclecount",
|
||||
url: "/cyclecount",
|
||||
icon: Package,
|
||||
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
|
||||
module: "logistics",
|
||||
active: true,
|
||||
},
|
||||
];
|
||||
|
||||
export function LogisticsSideBar({user, moduleID}: {user: User | null; moduleID: string}) {
|
||||
|
||||
@@ -15,7 +15,7 @@ const items = [
|
||||
title: "One Click Print",
|
||||
url: "/ocp",
|
||||
icon: Printer,
|
||||
role: ["systemAdmin"],
|
||||
role: ["viewer"],
|
||||
module: "ocp",
|
||||
active: true,
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import {Button} from "@/components/ui/button";
|
||||
import {CardHeader} from "@/components/ui/card";
|
||||
import {Input} from "@/components/ui/input";
|
||||
import {Label} from "@/components/ui/label";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import axios from "axios";
|
||||
import {useState} from "react";
|
||||
|
||||
@@ -12,27 +13,28 @@ import {toast} from "sonner";
|
||||
export default function ConsumeMaterial() {
|
||||
const {register: register1, handleSubmit: handleSubmit1, reset} = useForm();
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const token = localStorage.getItem("auth_token");
|
||||
const {token} = useSessionStore();
|
||||
|
||||
const handleConsume = async (data: any) => {
|
||||
setSubmitting(!submitting);
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const result = await axios.post(`/api/logistics/consume`, data, {
|
||||
headers: {Authorization: `Bearer ${token}`},
|
||||
});
|
||||
if (result.data.success) {
|
||||
toast.success(result.data.message);
|
||||
setSubmitting(!submitting);
|
||||
setSubmitting(false);
|
||||
reset();
|
||||
}
|
||||
if (!result.data.success) {
|
||||
//console.log(result.data);
|
||||
setSubmitting(!submitting);
|
||||
setSubmitting(false);
|
||||
|
||||
toast.error(result.data.message);
|
||||
}
|
||||
} catch (error: any) {
|
||||
//console.log(error);
|
||||
setSubmitting(!submitting);
|
||||
setSubmitting(false);
|
||||
if (error.status === 401) {
|
||||
toast.error("Unauthorized to do this task.");
|
||||
} else {
|
||||
|
||||
71
frontend/src/components/ocme/CycleCountLog.tsx
Normal file
71
frontend/src/components/ocme/CycleCountLog.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* we will do a very sloppy way for this just keep fetching the logs we spent to much time on this :()
|
||||
*/
|
||||
|
||||
// //import {useEffect, useState} from "react";
|
||||
// import {LstCard} from "../extendedUI/LstCard";
|
||||
// import {CardContent, CardHeader} from "../ui/card";
|
||||
// import {Skeleton} from "../ui/skeleton";
|
||||
// import {Button} from "../ui/button";
|
||||
// import {toast} from "sonner";
|
||||
// import {useEffect} from "react";
|
||||
|
||||
// export default function CycleCountLog() {
|
||||
// //const [logs, setLogs] = useState([]);
|
||||
// //const [streaming, setStreaming] = useState(false); // Track if streaming is active
|
||||
|
||||
// useEffect(() => {
|
||||
// // Start streaming when the button is clicked
|
||||
// let es;
|
||||
|
||||
// const url = `http://localhost:4000/api/logger/logs/stream?service=ocme-count&level=info`;
|
||||
|
||||
// es = new EventSource(url);
|
||||
// es.onopen = () => console.log(">>> Connection opened!");
|
||||
// es.onerror = (e) => console.log("ERROR!", e);
|
||||
// es.onmessage = (e) => {
|
||||
// const data = JSON.parse(e.data);
|
||||
|
||||
// console.log(e);
|
||||
// console.log(data);
|
||||
// switch (data.type) {
|
||||
// case "time-update":
|
||||
// console.log(data);
|
||||
// break;
|
||||
// case "error":
|
||||
// console.log(data);
|
||||
// break;
|
||||
// case "done":
|
||||
// console.log(data);
|
||||
// es.close(); // Close the connection when done
|
||||
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// };
|
||||
|
||||
// return () => es.close();
|
||||
// }, []);
|
||||
|
||||
// // const handleStartStreaming = () => {
|
||||
// // setStreaming(true); // Start streaming when button is clicked
|
||||
// // };
|
||||
// return (
|
||||
// <LstCard className="w-48">
|
||||
// <CardHeader className="flex justify-center">
|
||||
// <span>Cycle Count logs</span>
|
||||
// </CardHeader>
|
||||
// <CardContent>
|
||||
// {Array(10)
|
||||
// .fill(0)
|
||||
// .map((_, i) => (
|
||||
// <div key={i}>
|
||||
// <Skeleton className="m-2 h-4" />
|
||||
// </div>
|
||||
// ))}
|
||||
// </CardContent>
|
||||
// <Button onClick={() => toast.success("SOmething")}>Start Stream</Button>
|
||||
// </LstCard>
|
||||
// );
|
||||
// }
|
||||
185
frontend/src/components/ocme/ocmeCycleCount.tsx
Normal file
185
frontend/src/components/ocme/ocmeCycleCount.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
import {toast} from "sonner";
|
||||
import {LstCard} from "../extendedUI/LstCard";
|
||||
import {Button} from "../ui/button";
|
||||
import {Input} from "../ui/input";
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "../ui/table";
|
||||
import {Skeleton} from "../ui/skeleton";
|
||||
//import CycleCountLog from "./CycleCountLog";
|
||||
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "../ui/select";
|
||||
import {Controller, useForm} from "react-hook-form";
|
||||
import axios from "axios";
|
||||
import {useState} from "react";
|
||||
|
||||
export default function OcmeCycleCount() {
|
||||
const token = localStorage.getItem("auth_token");
|
||||
const [data, setData] = useState([]);
|
||||
const [counting, setCounting] = useState(false);
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
//watch,
|
||||
formState: {errors},
|
||||
reset,
|
||||
control,
|
||||
} = useForm();
|
||||
|
||||
const onSubmit = async (data: any) => {
|
||||
setData([]);
|
||||
setCounting(true);
|
||||
toast.success(`Cycle count started`);
|
||||
try {
|
||||
const res = await axios.post("/ocme/api/v1/cyclecount", data, {
|
||||
headers: {Authorization: `Bearer ${token}`},
|
||||
});
|
||||
toast.success(res.data.message);
|
||||
setData(res.data.data);
|
||||
setCounting(false);
|
||||
reset();
|
||||
} catch (error) {
|
||||
toast.error("There was an error cycle counting");
|
||||
setCounting(false);
|
||||
reset();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex flex-row w-screen">
|
||||
<div className="m-2 w-5/6">
|
||||
<LstCard>
|
||||
<p className="ml-2">Please enter the name or laneID you want to cycle count.</p>
|
||||
<div>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex justify-between">
|
||||
<div className="m-2 flex flex-row">
|
||||
<Input
|
||||
placeholder="enter lane: L064"
|
||||
className={errors.lane ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.lane}
|
||||
{...register("lane", {
|
||||
required: true,
|
||||
minLength: {
|
||||
value: 3,
|
||||
message: "The lane is too short!",
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<div className="ml-2">
|
||||
<Controller
|
||||
control={control}
|
||||
name="laneType"
|
||||
defaultValue={""}
|
||||
render={({
|
||||
field: {onChange},
|
||||
fieldState: {},
|
||||
//formState,
|
||||
}) => (
|
||||
<Select onValueChange={onChange}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Select name or id" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="name">Name</SelectItem>
|
||||
<SelectItem value="laneId">Lane ID</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button className="m-2" type="submit" disabled={counting}>
|
||||
{counting ? <span>Counting...</span> : <span>CycleCount</span>}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>LaneID</TableHead>
|
||||
<TableHead>Lane</TableHead>
|
||||
<TableHead>AV</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
<TableHead>Running Number</TableHead>
|
||||
<TableHead>In Ocme</TableHead>
|
||||
<TableHead>In Stock</TableHead>
|
||||
<TableHead>Result</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{data.length === 0 ? (
|
||||
<TableBody>
|
||||
{Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
) : (
|
||||
<>
|
||||
{data.map((i: any) => {
|
||||
let classname = ``;
|
||||
if (i.info === "Quality Check Required") {
|
||||
classname = `bg-red-500`;
|
||||
}
|
||||
if (i.info === "Sent to Inv") {
|
||||
classname = `bg-amber-700`;
|
||||
}
|
||||
return (
|
||||
<TableRow key={i.runningNumber}>
|
||||
<TableCell className={`font-medium ${classname}`}>
|
||||
{i.alpla_laneID}
|
||||
</TableCell>
|
||||
<TableCell className={`font-medium ${classname}`}>
|
||||
{i.alpla_laneDescription}
|
||||
</TableCell>
|
||||
<TableCell className={`font-medium ${classname}`}>
|
||||
{i.Article}
|
||||
</TableCell>
|
||||
<TableCell className={`font-medium ${classname}`}>
|
||||
{i.alpla_laneDescription}
|
||||
</TableCell>
|
||||
<TableCell className={`font-medium ${classname}`}>
|
||||
{i.runningNumber}
|
||||
</TableCell>
|
||||
<TableCell className={`font-medium ${classname}`}>{i.ocme}</TableCell>
|
||||
<TableCell className={`font-medium ${classname}`}>{i.stock}</TableCell>
|
||||
<TableCell className={`font-medium ${classname}`}>{i.info}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
</LstCard>
|
||||
</div>
|
||||
{/* <div className="m-2">
|
||||
<CycleCountLog />
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,125 @@
|
||||
import {LstCard} from "@/components/extendedUI/LstCard";
|
||||
|
||||
import {Skeleton} from "@/components/ui/skeleton";
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
// import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
// import {useSettingStore} from "@/lib/store/useSettings";
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import {getlabels} from "@/utils/querys/production/labels";
|
||||
import {format} from "date-fns";
|
||||
|
||||
const labelLogs = [
|
||||
{key: "line", label: "Line"},
|
||||
{key: "printerName", label: "Printer"},
|
||||
{key: "runningNr", label: "Running #"},
|
||||
{key: "upd_date", label: "Label date"},
|
||||
{key: "status", label: "Label Status"},
|
||||
//{key: "reprint", label: "Reprint"}, // removing the reprint button for now until repritning is working as intended
|
||||
];
|
||||
export default function LabelLog() {
|
||||
return <LstCard className="m-2 p-2"> label logs here</LstCard>;
|
||||
const {data, isError, isLoading} = useQuery(getlabels("4"));
|
||||
//const {user} = useSessionStore();
|
||||
//const {settings} = useSettingStore();
|
||||
//const server = settings.filter((n) => n.name === "server")[0]?.value || "";
|
||||
|
||||
//const roles = ["admin", "manager", "operator"];
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="m-2 p-2 min-h-2/5">
|
||||
<LstCard>
|
||||
<p className="text-center">Labels for the last 2 hours</p>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{labelLogs.map((l) => (
|
||||
<TableHead key={l.key}>{l.label}</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{Array(7)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</LstCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<LstCard className="m-2 p-2 min-h-2/5">
|
||||
<p className="text-center">Labels for the last 2 hours</p>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{labelLogs.map((l) => (
|
||||
<TableHead key={l.key}>{l.label}</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<TableBody>
|
||||
{Array(7)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<TableBody>
|
||||
{data?.map((label: any) => (
|
||||
<TableRow key={label.runningNr}>
|
||||
<TableCell className="font-medium">{label.line}</TableCell>
|
||||
<TableCell className="font-medium">{label.printerName}</TableCell>
|
||||
<TableCell className="font-medium">{label.runningNr}</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
{format(label.upd_date, "M/d/yyyy hh:mm")}
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">{label.status}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,222 @@
|
||||
import {LstCard} from "@/components/extendedUI/LstCard";
|
||||
|
||||
import {Skeleton} from "@/components/ui/skeleton";
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import {useSettingStore} from "@/lib/store/useSettings";
|
||||
import {LotType} from "@/types/lots";
|
||||
import {getlots} from "@/utils/querys/production/lots";
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import ManualPrint from "./ManualPrinting/ManualPrint";
|
||||
import ManualPrintForm from "./ManualPrinting/ManualPrintForm";
|
||||
|
||||
let lotColumns = [
|
||||
{
|
||||
key: "MachineDescription",
|
||||
label: "Machine",
|
||||
},
|
||||
{
|
||||
key: "AV",
|
||||
label: "AV",
|
||||
},
|
||||
{
|
||||
key: "Alias",
|
||||
label: "AvDescription",
|
||||
},
|
||||
{
|
||||
key: "LOT",
|
||||
label: "LotNumber",
|
||||
},
|
||||
{
|
||||
key: "ProlinkLot",
|
||||
label: "ProlinkLot",
|
||||
},
|
||||
{
|
||||
key: "PlannedQTY",
|
||||
label: "PlannedQTY",
|
||||
},
|
||||
{
|
||||
key: "Produced",
|
||||
label: "Produced",
|
||||
},
|
||||
{
|
||||
key: "Remaining",
|
||||
label: "Remaining",
|
||||
},
|
||||
{
|
||||
key: "overPrinting",
|
||||
label: "Overprinting",
|
||||
},
|
||||
// {
|
||||
// key: "lastProlinkUpdate",
|
||||
// label: "Last ProlinkCheck",
|
||||
// },
|
||||
// {
|
||||
// key: "printLabel",
|
||||
// label: "Print Label",
|
||||
// },
|
||||
];
|
||||
export default function Lots() {
|
||||
const {data, isError, isLoading} = useQuery(getlots());
|
||||
const {user} = useSessionStore();
|
||||
const {settings} = useSettingStore();
|
||||
const server = settings.filter((n) => n.name === "server")[0]?.value || "";
|
||||
|
||||
console.log(server);
|
||||
|
||||
const roles = ["admin", "manager", "operator"];
|
||||
|
||||
if (user && roles.includes(user.role)) {
|
||||
//width = 1280;
|
||||
const checkCol = lotColumns.some((l) => l.key === "printLabel");
|
||||
if (!checkCol) {
|
||||
lotColumns = [
|
||||
...lotColumns,
|
||||
{
|
||||
key: "printLabel",
|
||||
label: "Print Label",
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="m-2 p-2 min-h-2/5">
|
||||
<LstCard>
|
||||
<p className="text-center">Current Assigned lots</p>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{lotColumns.map((l) => (
|
||||
<TableHead key={l.key}>{l.label}</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</LstCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<LstCard className="m-2 p-2 min-h-2/5">
|
||||
<h1>Lots</h1>
|
||||
<p className="text-center">Current Assigned lots</p>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{lotColumns.map((l) => (
|
||||
<TableHead key={l.key}>{l.label}</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<TableBody>
|
||||
{Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<TableBody>
|
||||
{data?.map((lot: LotType) => (
|
||||
<TableRow key={lot.LabelOnlineID}>
|
||||
<TableCell className="font-medium">{lot.MachineLocation}</TableCell>
|
||||
<TableCell className="font-medium">{lot.AV}</TableCell>
|
||||
<TableCell className="font-medium">{lot.Alias}</TableCell>
|
||||
<TableCell className="font-medium">{lot.LOT}</TableCell>
|
||||
<TableCell className="font-medium">{lot.ProlinkLot}</TableCell>
|
||||
<TableCell className="font-medium">{lot.PlannedQTY}</TableCell>
|
||||
<TableCell className="font-medium">{lot.Produced}</TableCell>
|
||||
<TableCell className="font-medium">{lot.Remaining}</TableCell>
|
||||
<TableCell className="font-medium">{lot.overPrinting}</TableCell>
|
||||
{user && roles.includes(user.role) && (
|
||||
<>
|
||||
{server === "usday1vms006" || server === "localhost" ? (
|
||||
<>
|
||||
<TableCell className="flex justify-center">
|
||||
<ManualPrintForm lot={lot} />
|
||||
</TableCell>
|
||||
</>
|
||||
) : (
|
||||
<TableCell className="flex justify-center">
|
||||
<ManualPrint lot={lot} />
|
||||
</TableCell>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
//import {useSettingStore} from "@/lib/store/useSettings";
|
||||
import {LotType} from "@/types/lots";
|
||||
import {Tag} from "lucide-react";
|
||||
import {toast} from "sonner";
|
||||
import {manualPrintLabels} from "./ManualPrintLabel";
|
||||
|
||||
export default function ManualPrint({lot}: {lot: LotType}) {
|
||||
const {user} = useSessionStore();
|
||||
//const {settings} = useSettingStore();
|
||||
//const server = settings.filter((n) => n.name === "server")[0]?.value;
|
||||
//const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
|
||||
//const serverUrl = `http://${server}:${serverPort}`;
|
||||
|
||||
const handlePrintLabel = async (lot: LotType) => {
|
||||
//console.log(lot);
|
||||
|
||||
const labels: any = await manualPrintLabels(lot, user);
|
||||
|
||||
if (labels.success) {
|
||||
toast.success(labels.message);
|
||||
} else {
|
||||
toast.error(labels.message);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Button variant="outline" size="icon" onClick={() => handlePrintLabel(lot)}>
|
||||
<Tag className="h-[16px] w-[16px]" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import {Input} from "@/components/ui/input";
|
||||
import {Label} from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {Textarea} from "@/components/ui/textarea";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import {useSettingStore} from "@/lib/store/useSettings";
|
||||
import {LotType} from "@/types/lots";
|
||||
import axios from "axios";
|
||||
import {Tag} from "lucide-react";
|
||||
import {useState} from "react";
|
||||
import {Controller, useForm} from "react-hook-form";
|
||||
import {toast} from "sonner";
|
||||
import {manualPrintLabels} from "./ManualPrintLabel";
|
||||
|
||||
const printReason = [
|
||||
{key: "printerIssue", label: "Printer Related"},
|
||||
{key: "strapper", label: "Strapper Error"},
|
||||
{key: "manualCheck", label: "20th pallet check"},
|
||||
{key: "outOfSync", label: "Labeler Out of Sync"},
|
||||
];
|
||||
|
||||
export default function ManualPrintForm({lot}: {lot: LotType}) {
|
||||
const {user} = useSessionStore();
|
||||
const token = localStorage.getItem("auth_token");
|
||||
const {settings} = useSettingStore();
|
||||
const [open, setOpen] = useState(false);
|
||||
const server = settings.filter((n) => n.name === "server")[0]?.value;
|
||||
// const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
|
||||
// const serverUrl = `http://${server}:${serverPort}`;
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
//watch,
|
||||
formState: {errors},
|
||||
reset,
|
||||
control,
|
||||
} = useForm();
|
||||
|
||||
const handlePrintLabel = async (lot: LotType) => {
|
||||
//console.log(lot);
|
||||
const labels: any = await manualPrintLabels(lot, user);
|
||||
|
||||
if (labels.success) {
|
||||
toast.success(labels.message);
|
||||
} else {
|
||||
toast.error(labels.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleManualPrintLog = async (logData: any, lot: LotType) => {
|
||||
// toast.success(`A new label was sent to printer: ${lot.PrinterName} for line ${lot.MachineDescription} `);
|
||||
const logdataUrl = `/api/ocp/manualLabelLog`;
|
||||
axios
|
||||
.post(logdataUrl, logData, {headers: {Authorization: `Bearer ${token}`}})
|
||||
.then((d) => {
|
||||
//console.log(d);
|
||||
toast.success(d.data.message);
|
||||
handlePrintLabel(lot);
|
||||
reset();
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response.status === 500) {
|
||||
toast.error(`Internal Server error please try again.`);
|
||||
return {sucess: false};
|
||||
}
|
||||
|
||||
if (e.response.status === 401) {
|
||||
//console.log(e.response);
|
||||
toast.error(`You are not authorized to do this.`);
|
||||
return {sucess: false};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = (data: any) => {
|
||||
console.log(data);
|
||||
|
||||
handleManualPrintLog(data, lot);
|
||||
};
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (!open) {
|
||||
reset();
|
||||
}
|
||||
setOpen(isOpen);
|
||||
// toast.message("Model was something", {
|
||||
// description: isOpen ? "Modal is open" : "Modal is closed",
|
||||
// });
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" size="icon">
|
||||
<Tag className="h-[16px] w-[16px]" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit profile</DialogTitle>
|
||||
<DialogDescription>
|
||||
Make changes to your profile here. Click save when you're done.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<p>
|
||||
To manually print a label you must complete all the required fields below.
|
||||
<br />
|
||||
If you clicked this in error just click close
|
||||
</p>
|
||||
<hr className="mt-2 mb-2" />
|
||||
{server == "usday1vms006" ? (
|
||||
<Controller
|
||||
control={control}
|
||||
name="printReason"
|
||||
defaultValue={""}
|
||||
render={({
|
||||
field: {onChange},
|
||||
fieldState: {},
|
||||
//formState,
|
||||
}) => (
|
||||
<Select onValueChange={onChange}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Select Reason" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Print Reasons</SelectLabel>
|
||||
{printReason.map((printReason: any) => (
|
||||
<SelectItem value={printReason.key}>{printReason.label}</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<div>
|
||||
<Label htmlFor="printRason" className="m-1">
|
||||
Why are you manually printing?
|
||||
</Label>
|
||||
<Input
|
||||
type="text"
|
||||
className={errors.printReason ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.printReason}
|
||||
{...register("printReason", {
|
||||
required: true,
|
||||
minLength: {
|
||||
value: 5,
|
||||
message: "To short of a reason please try again!",
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Label htmlFor="line" className="m-1">
|
||||
"What is the line number you are printing?"
|
||||
</Label>
|
||||
<Input
|
||||
//variant="underlined"
|
||||
type="number"
|
||||
className={errors.line ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.line}
|
||||
{...register("line", {required: true})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="initials" className="m-1">
|
||||
Enter intials
|
||||
</Label>
|
||||
<Input
|
||||
//variant="underlined"
|
||||
//label="Enter intials"
|
||||
|
||||
{...register("initials", {required: true})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
//label="Comments"
|
||||
placeholder="add more info as needed."
|
||||
{...register("additionalComments")}
|
||||
/>
|
||||
|
||||
<DialogFooter>
|
||||
<Button color="danger" variant="default" onClick={() => setOpen(!open)}>
|
||||
Close
|
||||
</Button>
|
||||
<Button color="primary" type="submit">
|
||||
Print
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import {LotType} from "@/types/lots";
|
||||
import axios from "axios";
|
||||
|
||||
export const manualPrintLabels = async (lot: LotType, user: any) => {
|
||||
//console.log(lot);
|
||||
const labelUrl = `/ocp/manualPrintAndFollow`;
|
||||
|
||||
try {
|
||||
const res = await axios.post(
|
||||
labelUrl,
|
||||
{line: lot.MachineLocation, printerName: lot.PrinterName},
|
||||
{headers: {Authorization: `Basic ${user?.prod}`}}
|
||||
);
|
||||
|
||||
if (res.data.success) {
|
||||
return {
|
||||
success: true,
|
||||
message: `A new label was printed for ${lot.MachineDescription} to printer: ${lot.PrinterName}`,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
message: `Line ${lot.MachineDescription} encountered an error printing labels: ${res.data.message}`,
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.response.status === 500) {
|
||||
//toast.error(`Internal Server error please try again.`);
|
||||
return {
|
||||
success: false,
|
||||
message: `Internal Server error please try again.`,
|
||||
};
|
||||
}
|
||||
|
||||
if (error.response.status === 401) {
|
||||
//console.log(e.response);
|
||||
//toast.error(`You are not authorized to do this.`);
|
||||
return {
|
||||
success: false,
|
||||
message: `You are not authorized to do this.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,52 @@
|
||||
import {LstCard} from "@/components/extendedUI/LstCard";
|
||||
import {Skeleton} from "@/components/ui/skeleton";
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
|
||||
let printerCols = [
|
||||
{
|
||||
key: "status",
|
||||
label: "Status",
|
||||
},
|
||||
{
|
||||
key: "printer",
|
||||
label: "Printer",
|
||||
},
|
||||
{
|
||||
key: "statusMessage",
|
||||
label: "Status Message",
|
||||
},
|
||||
];
|
||||
export default function PrinterStatus() {
|
||||
return <LstCard className="m-2 p-2">Printer Status</LstCard>;
|
||||
return (
|
||||
<LstCard className="m-2 p-2">
|
||||
<p className="text-center">Printer Status</p>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{printerCols.map((l) => (
|
||||
<TableHead key={l.key}>{l.label}</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
|
||||
import {useModuleStore} from "../../lib/store/useModuleStore";
|
||||
import {useEffect} from "react";
|
||||
import {useSettingStore} from "@/lib/store/useSettings";
|
||||
//import {useGetUserRoles} from "@/lib/store/useGetRoles";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export const SessionProvider = ({children}: {children: React.ReactNode}) => {
|
||||
const {fetchModules} = useModuleStore();
|
||||
const {fetchSettings} = useSettingStore();
|
||||
//const {fetchUserRoles} = useGetUserRoles();
|
||||
|
||||
useEffect(() => {
|
||||
fetchModules();
|
||||
fetchSettings();
|
||||
//fetchUserRoles();
|
||||
}, []);
|
||||
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
|
||||
|
||||
@@ -5,19 +5,20 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
|
||||
183
frontend/src/components/ui/select.tsx
Normal file
183
frontend/src/components/ui/select.tsx
Normal file
@@ -0,0 +1,183 @@
|
||||
import * as React from "react"
|
||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Select({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
||||
return <SelectPrimitive.Root data-slot="select" {...props} />
|
||||
}
|
||||
|
||||
function SelectGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
||||
return <SelectPrimitive.Group data-slot="select-group" {...props} />
|
||||
}
|
||||
|
||||
function SelectValue({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
||||
return <SelectPrimitive.Value data-slot="select-value" {...props} />
|
||||
}
|
||||
|
||||
function SelectTrigger({
|
||||
className,
|
||||
size = "default",
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
||||
size?: "sm" | "default"
|
||||
}) {
|
||||
return (
|
||||
<SelectPrimitive.Trigger
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDownIcon className="size-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectContent({
|
||||
className,
|
||||
children,
|
||||
position = "popper",
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
||||
return (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectLabel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
||||
return (
|
||||
<SelectPrimitive.Label
|
||||
data-slot="select-label"
|
||||
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
||||
return (
|
||||
<SelectPrimitive.Item
|
||||
data-slot="select-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute right-2 flex size-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
||||
return (
|
||||
<SelectPrimitive.Separator
|
||||
data-slot="select-separator"
|
||||
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectScrollUpButton({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
data-slot="select-scroll-up-button"
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectScrollDownButton({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
data-slot="select-scroll-down-button"
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
}
|
||||
18
frontend/src/components/ui/textarea.tsx
Normal file
18
frontend/src/components/ui/textarea.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
return (
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Textarea }
|
||||
@@ -44,7 +44,7 @@ function TooltipContent({
|
||||
data-slot="tooltip-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
|
||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -17,6 +17,16 @@ const fetchSession = async () => {
|
||||
});
|
||||
// console.log(res);
|
||||
if (!res.ok) {
|
||||
localStorage.removeItem("auth_token");
|
||||
// remove these for a while if no session just until fully to 2.0 and clearly no one has ran lstv1 in a long time
|
||||
localStorage.removeItem("ally-supports-cache");
|
||||
localStorage.removeItem("auth-storage");
|
||||
localStorage.removeItem("nextauth.message");
|
||||
localStorage.removeItem("prod");
|
||||
localStorage.removeItem("cards");
|
||||
localStorage.removeItem("rememberMe");
|
||||
localStorage.removeItem("username");
|
||||
|
||||
throw new Error("Session not found");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {User} from "@/types/users";
|
||||
import axios from "axios";
|
||||
import {create} from "zustand";
|
||||
|
||||
export type SessionState = {
|
||||
@@ -16,9 +17,14 @@ export const useSessionStore = create<SessionState>((set) => {
|
||||
user: null, // User is NOT stored in localStorage
|
||||
token: storedToken || null,
|
||||
|
||||
setSession: (user, token) => {
|
||||
setSession: async (user: any, token) => {
|
||||
if (token) {
|
||||
localStorage.setItem("auth_token", token);
|
||||
const response = await axios.get("/api/auth/getuseraccess", {
|
||||
headers: {Authorization: `Bearer ${token}`},
|
||||
});
|
||||
const data = response.data; //await response.json();
|
||||
user = {...user, roles: data.data};
|
||||
} else {
|
||||
localStorage.removeItem("auth_token");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {create} from "zustand";
|
||||
import {useSessionStore} from "./sessionStore";
|
||||
import {Modules} from "@/types/modules";
|
||||
import axios from "axios";
|
||||
|
||||
interface SettingState {
|
||||
userRoles: Modules[];
|
||||
@@ -19,15 +20,8 @@ export const useGetUserRoles = create<SettingState>()((set) => ({
|
||||
try {
|
||||
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
|
||||
const {token} = useSessionStore();
|
||||
const response = await fetch(`/api/auth/getuseraccess`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authentication: `Beaer ${token}`,
|
||||
// You can add other headers here if necessary
|
||||
},
|
||||
});
|
||||
const data: FetchModulesResponse = await response.json();
|
||||
const response = await axios.get("/api/auth/getuseraccess", {headers: {Authorization: `Bearer ${token}`}});
|
||||
const data: FetchModulesResponse = response.data; //await response.json();
|
||||
//console.log(data);
|
||||
set({userRoles: data.data});
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {Modules} from "@/types/modules";
|
||||
import axios from "axios";
|
||||
import {create} from "zustand";
|
||||
|
||||
interface SettingState {
|
||||
@@ -17,18 +18,13 @@ export const useModuleStore = create<SettingState>()((set) => ({
|
||||
fetchModules: async () => {
|
||||
try {
|
||||
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
|
||||
const response = await fetch(`/api/server/modules`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
// You can add other headers here if necessary
|
||||
},
|
||||
});
|
||||
const data: FetchModulesResponse = await response.json();
|
||||
const response = await axios.get(`/api/server/modules`, {});
|
||||
const data: FetchModulesResponse = response.data; //await response.json();
|
||||
//console.log(data);
|
||||
set({modules: data.data});
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch settings:", error);
|
||||
set({modules: []});
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
29
frontend/src/lib/store/useSettings.ts
Normal file
29
frontend/src/lib/store/useSettings.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import axios from "axios";
|
||||
import {create} from "zustand";
|
||||
|
||||
interface SettingState {
|
||||
settings: any[];
|
||||
|
||||
fetchSettings: () => Promise<void>;
|
||||
setSettings: (settings: any[]) => void;
|
||||
}
|
||||
interface FetchModulesResponse {
|
||||
data: any[];
|
||||
}
|
||||
|
||||
export const useSettingStore = create<SettingState>()((set) => ({
|
||||
settings: [],
|
||||
setSettings: (settings) => set({settings}),
|
||||
fetchSettings: async () => {
|
||||
try {
|
||||
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
|
||||
const response = await axios.get(`/api/server/settings`, {});
|
||||
const data: FetchModulesResponse = response.data; //await response.json();
|
||||
//console.log(data);
|
||||
set({settings: data.data});
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch settings:", error);
|
||||
set({settings: []});
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -18,11 +18,12 @@ import { Route as AuthImport } from './routes/_auth'
|
||||
import { Route as AdminImport } from './routes/_admin'
|
||||
import { Route as IndexImport } from './routes/index'
|
||||
import { Route as OcpIndexImport } from './routes/ocp/index'
|
||||
import { Route as OcpLotsImport } from './routes/ocp/lots'
|
||||
import { Route as EomEomImport } from './routes/_eom/eom'
|
||||
import { Route as AuthProfileImport } from './routes/_auth/profile'
|
||||
import { Route as AdminSettingsImport } from './routes/_admin/settings'
|
||||
import { Route as AdminServersImport } from './routes/_admin/servers'
|
||||
import { Route as AdminModulesImport } from './routes/_admin/modules'
|
||||
import { Route as ocmeCyclecountIndexImport } from './routes/(ocme)/cyclecount/index'
|
||||
import { Route as logisticsMaterialHelperIndexImport } from './routes/(logistics)/materialHelper/index'
|
||||
import { Route as EomArticleAvImport } from './routes/_eom/article/$av'
|
||||
import { Route as logisticsMaterialHelperSiloLinkIndexImport } from './routes/(logistics)/materialHelper/siloLink/index'
|
||||
@@ -69,12 +70,6 @@ const OcpIndexRoute = OcpIndexImport.update({
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const OcpLotsRoute = OcpLotsImport.update({
|
||||
id: '/ocp/lots',
|
||||
path: '/ocp/lots',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const EomEomRoute = EomEomImport.update({
|
||||
id: '/eom',
|
||||
path: '/eom',
|
||||
@@ -93,12 +88,24 @@ const AdminSettingsRoute = AdminSettingsImport.update({
|
||||
getParentRoute: () => AdminRoute,
|
||||
} as any)
|
||||
|
||||
const AdminServersRoute = AdminServersImport.update({
|
||||
id: '/servers',
|
||||
path: '/servers',
|
||||
getParentRoute: () => AdminRoute,
|
||||
} as any)
|
||||
|
||||
const AdminModulesRoute = AdminModulesImport.update({
|
||||
id: '/modules',
|
||||
path: '/modules',
|
||||
getParentRoute: () => AdminRoute,
|
||||
} as any)
|
||||
|
||||
const ocmeCyclecountIndexRoute = ocmeCyclecountIndexImport.update({
|
||||
id: '/(ocme)/cyclecount/',
|
||||
path: '/cyclecount/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const logisticsMaterialHelperIndexRoute =
|
||||
logisticsMaterialHelperIndexImport.update({
|
||||
id: '/(logistics)/materialHelper/',
|
||||
@@ -179,6 +186,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof AdminModulesImport
|
||||
parentRoute: typeof AdminImport
|
||||
}
|
||||
'/_admin/servers': {
|
||||
id: '/_admin/servers'
|
||||
path: '/servers'
|
||||
fullPath: '/servers'
|
||||
preLoaderRoute: typeof AdminServersImport
|
||||
parentRoute: typeof AdminImport
|
||||
}
|
||||
'/_admin/settings': {
|
||||
id: '/_admin/settings'
|
||||
path: '/settings'
|
||||
@@ -200,13 +214,6 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof EomEomImport
|
||||
parentRoute: typeof EomImport
|
||||
}
|
||||
'/ocp/lots': {
|
||||
id: '/ocp/lots'
|
||||
path: '/ocp/lots'
|
||||
fullPath: '/ocp/lots'
|
||||
preLoaderRoute: typeof OcpLotsImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/ocp/': {
|
||||
id: '/ocp/'
|
||||
path: '/ocp'
|
||||
@@ -228,6 +235,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof logisticsMaterialHelperIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/(ocme)/cyclecount/': {
|
||||
id: '/(ocme)/cyclecount/'
|
||||
path: '/cyclecount'
|
||||
fullPath: '/cyclecount'
|
||||
preLoaderRoute: typeof ocmeCyclecountIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/(logistics)/materialHelper/consumption/': {
|
||||
id: '/(logistics)/materialHelper/consumption/'
|
||||
path: '/materialHelper/consumption'
|
||||
@@ -249,11 +263,13 @@ declare module '@tanstack/react-router' {
|
||||
|
||||
interface AdminRouteChildren {
|
||||
AdminModulesRoute: typeof AdminModulesRoute
|
||||
AdminServersRoute: typeof AdminServersRoute
|
||||
AdminSettingsRoute: typeof AdminSettingsRoute
|
||||
}
|
||||
|
||||
const AdminRouteChildren: AdminRouteChildren = {
|
||||
AdminModulesRoute: AdminModulesRoute,
|
||||
AdminServersRoute: AdminServersRoute,
|
||||
AdminSettingsRoute: AdminSettingsRoute,
|
||||
}
|
||||
|
||||
@@ -287,13 +303,14 @@ export interface FileRoutesByFullPath {
|
||||
'/about': typeof AboutRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/modules': typeof AdminModulesRoute
|
||||
'/servers': typeof AdminServersRoute
|
||||
'/settings': typeof AdminSettingsRoute
|
||||
'/profile': typeof AuthProfileRoute
|
||||
'/eom': typeof EomEomRoute
|
||||
'/ocp/lots': typeof OcpLotsRoute
|
||||
'/ocp': typeof OcpIndexRoute
|
||||
'/article/$av': typeof EomArticleAvRoute
|
||||
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
|
||||
'/cyclecount': typeof ocmeCyclecountIndexRoute
|
||||
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -304,13 +321,14 @@ export interface FileRoutesByTo {
|
||||
'/about': typeof AboutRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/modules': typeof AdminModulesRoute
|
||||
'/servers': typeof AdminServersRoute
|
||||
'/settings': typeof AdminSettingsRoute
|
||||
'/profile': typeof AuthProfileRoute
|
||||
'/eom': typeof EomEomRoute
|
||||
'/ocp/lots': typeof OcpLotsRoute
|
||||
'/ocp': typeof OcpIndexRoute
|
||||
'/article/$av': typeof EomArticleAvRoute
|
||||
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
|
||||
'/cyclecount': typeof ocmeCyclecountIndexRoute
|
||||
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -324,13 +342,14 @@ export interface FileRoutesById {
|
||||
'/about': typeof AboutRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/_admin/modules': typeof AdminModulesRoute
|
||||
'/_admin/servers': typeof AdminServersRoute
|
||||
'/_admin/settings': typeof AdminSettingsRoute
|
||||
'/_auth/profile': typeof AuthProfileRoute
|
||||
'/_eom/eom': typeof EomEomRoute
|
||||
'/ocp/lots': typeof OcpLotsRoute
|
||||
'/ocp/': typeof OcpIndexRoute
|
||||
'/_eom/article/$av': typeof EomArticleAvRoute
|
||||
'/(logistics)/materialHelper/': typeof logisticsMaterialHelperIndexRoute
|
||||
'/(ocme)/cyclecount/': typeof ocmeCyclecountIndexRoute
|
||||
'/(logistics)/materialHelper/consumption/': typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
'/(logistics)/materialHelper/siloLink/': typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -343,13 +362,14 @@ export interface FileRouteTypes {
|
||||
| '/about'
|
||||
| '/login'
|
||||
| '/modules'
|
||||
| '/servers'
|
||||
| '/settings'
|
||||
| '/profile'
|
||||
| '/eom'
|
||||
| '/ocp/lots'
|
||||
| '/ocp'
|
||||
| '/article/$av'
|
||||
| '/materialHelper'
|
||||
| '/cyclecount'
|
||||
| '/materialHelper/consumption'
|
||||
| '/materialHelper/siloLink'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
@@ -359,13 +379,14 @@ export interface FileRouteTypes {
|
||||
| '/about'
|
||||
| '/login'
|
||||
| '/modules'
|
||||
| '/servers'
|
||||
| '/settings'
|
||||
| '/profile'
|
||||
| '/eom'
|
||||
| '/ocp/lots'
|
||||
| '/ocp'
|
||||
| '/article/$av'
|
||||
| '/materialHelper'
|
||||
| '/cyclecount'
|
||||
| '/materialHelper/consumption'
|
||||
| '/materialHelper/siloLink'
|
||||
id:
|
||||
@@ -377,13 +398,14 @@ export interface FileRouteTypes {
|
||||
| '/about'
|
||||
| '/login'
|
||||
| '/_admin/modules'
|
||||
| '/_admin/servers'
|
||||
| '/_admin/settings'
|
||||
| '/_auth/profile'
|
||||
| '/_eom/eom'
|
||||
| '/ocp/lots'
|
||||
| '/ocp/'
|
||||
| '/_eom/article/$av'
|
||||
| '/(logistics)/materialHelper/'
|
||||
| '/(ocme)/cyclecount/'
|
||||
| '/(logistics)/materialHelper/consumption/'
|
||||
| '/(logistics)/materialHelper/siloLink/'
|
||||
fileRoutesById: FileRoutesById
|
||||
@@ -396,9 +418,9 @@ export interface RootRouteChildren {
|
||||
EomRoute: typeof EomRouteWithChildren
|
||||
AboutRoute: typeof AboutRoute
|
||||
LoginRoute: typeof LoginRoute
|
||||
OcpLotsRoute: typeof OcpLotsRoute
|
||||
OcpIndexRoute: typeof OcpIndexRoute
|
||||
logisticsMaterialHelperIndexRoute: typeof logisticsMaterialHelperIndexRoute
|
||||
ocmeCyclecountIndexRoute: typeof ocmeCyclecountIndexRoute
|
||||
logisticsMaterialHelperConsumptionIndexRoute: typeof logisticsMaterialHelperConsumptionIndexRoute
|
||||
logisticsMaterialHelperSiloLinkIndexRoute: typeof logisticsMaterialHelperSiloLinkIndexRoute
|
||||
}
|
||||
@@ -410,9 +432,9 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
EomRoute: EomRouteWithChildren,
|
||||
AboutRoute: AboutRoute,
|
||||
LoginRoute: LoginRoute,
|
||||
OcpLotsRoute: OcpLotsRoute,
|
||||
OcpIndexRoute: OcpIndexRoute,
|
||||
logisticsMaterialHelperIndexRoute: logisticsMaterialHelperIndexRoute,
|
||||
ocmeCyclecountIndexRoute: ocmeCyclecountIndexRoute,
|
||||
logisticsMaterialHelperConsumptionIndexRoute:
|
||||
logisticsMaterialHelperConsumptionIndexRoute,
|
||||
logisticsMaterialHelperSiloLinkIndexRoute:
|
||||
@@ -435,9 +457,9 @@ export const routeTree = rootRoute
|
||||
"/_eom",
|
||||
"/about",
|
||||
"/login",
|
||||
"/ocp/lots",
|
||||
"/ocp/",
|
||||
"/(logistics)/materialHelper/",
|
||||
"/(ocme)/cyclecount/",
|
||||
"/(logistics)/materialHelper/consumption/",
|
||||
"/(logistics)/materialHelper/siloLink/"
|
||||
]
|
||||
@@ -449,6 +471,7 @@ export const routeTree = rootRoute
|
||||
"filePath": "_admin.tsx",
|
||||
"children": [
|
||||
"/_admin/modules",
|
||||
"/_admin/servers",
|
||||
"/_admin/settings"
|
||||
]
|
||||
},
|
||||
@@ -475,6 +498,10 @@ export const routeTree = rootRoute
|
||||
"filePath": "_admin/modules.tsx",
|
||||
"parent": "/_admin"
|
||||
},
|
||||
"/_admin/servers": {
|
||||
"filePath": "_admin/servers.tsx",
|
||||
"parent": "/_admin"
|
||||
},
|
||||
"/_admin/settings": {
|
||||
"filePath": "_admin/settings.tsx",
|
||||
"parent": "/_admin"
|
||||
@@ -487,9 +514,6 @@ export const routeTree = rootRoute
|
||||
"filePath": "_eom/eom.tsx",
|
||||
"parent": "/_eom"
|
||||
},
|
||||
"/ocp/lots": {
|
||||
"filePath": "ocp/lots.tsx"
|
||||
},
|
||||
"/ocp/": {
|
||||
"filePath": "ocp/index.tsx"
|
||||
},
|
||||
@@ -500,6 +524,9 @@ export const routeTree = rootRoute
|
||||
"/(logistics)/materialHelper/": {
|
||||
"filePath": "(logistics)/materialHelper/index.tsx"
|
||||
},
|
||||
"/(ocme)/cyclecount/": {
|
||||
"filePath": "(ocme)/cyclecount/index.tsx"
|
||||
},
|
||||
"/(logistics)/materialHelper/consumption/": {
|
||||
"filePath": "(logistics)/materialHelper/consumption/index.tsx"
|
||||
},
|
||||
|
||||
14
frontend/src/routes/(ocme)/cyclecount/index.tsx
Normal file
14
frontend/src/routes/(ocme)/cyclecount/index.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import OcmeCycleCount from "@/components/ocme/ocmeCycleCount";
|
||||
import {createFileRoute} from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/(ocme)/cyclecount/")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="m-2">
|
||||
<OcmeCycleCount />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,91 +1,94 @@
|
||||
import {createRootRoute, Link, Outlet} from "@tanstack/react-router";
|
||||
import {TanStackRouterDevtools} from "@tanstack/router-devtools";
|
||||
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
|
||||
//import {TanStackRouterDevtools} from "@tanstack/router-devtools";
|
||||
import Cookies from "js-cookie";
|
||||
import {SidebarProvider} from "../components/ui/sidebar";
|
||||
import {ThemeProvider} from "../components/layout/theme-provider";
|
||||
import {ModeToggle} from "../components/layout/mode-toggle";
|
||||
import {AppSidebar} from "../components/layout/lst-sidebar";
|
||||
import {Avatar, AvatarFallback, AvatarImage} from "../components/ui/avatar";
|
||||
import { SidebarProvider } from "../components/ui/sidebar";
|
||||
import { ThemeProvider } from "../components/layout/theme-provider";
|
||||
import { ModeToggle } from "../components/layout/mode-toggle";
|
||||
import { AppSidebar } from "../components/layout/lst-sidebar";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "../components/ui/avatar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "../components/ui/dropdown-menu";
|
||||
import {SessionProvider} from "../components/providers/Providers";
|
||||
import {Toaster} from "sonner";
|
||||
import {Button} from "../components/ui/button";
|
||||
import { SessionProvider } from "../components/providers/Providers";
|
||||
import { Toaster } from "sonner";
|
||||
//import { Button } from "../components/ui/button";
|
||||
|
||||
import {useSessionStore} from "../lib/store/sessionStore";
|
||||
import {useSession} from "@/hooks/useSession";
|
||||
import {useLogout} from "@/hooks/useLogout";
|
||||
import { useSessionStore } from "../lib/store/sessionStore";
|
||||
import { useSession } from "@/hooks/useSession";
|
||||
import { useLogout } from "@/hooks/useLogout";
|
||||
|
||||
// same as the layout
|
||||
export const Route = createRootRoute({
|
||||
component: () => {
|
||||
const sidebarState = Cookies.get("sidebar_state") === "true";
|
||||
const {session} = useSession();
|
||||
const {user} = useSessionStore();
|
||||
const logout = useLogout();
|
||||
component: () => {
|
||||
const sidebarState = Cookies.get("sidebar_state") === "true";
|
||||
const { session } = useSession();
|
||||
const { user } = useSessionStore();
|
||||
const logout = useLogout();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SessionProvider>
|
||||
<ThemeProvider>
|
||||
<nav className="flex justify-end">
|
||||
<div className="m-2 flex flex-row">
|
||||
<div className="m-auto pr-2">
|
||||
<p>Add Card</p>
|
||||
</div>
|
||||
<div className="m-1">
|
||||
<ModeToggle />
|
||||
</div>
|
||||
{session ? (
|
||||
<div className="m-1">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Avatar>
|
||||
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel>Hello {user?.username}</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>Profile</DropdownMenuItem>
|
||||
return (
|
||||
<>
|
||||
<SessionProvider>
|
||||
<ThemeProvider>
|
||||
<nav className="flex justify-end">
|
||||
<div className="m-2 flex flex-row">
|
||||
<div className="m-auto pr-2">
|
||||
<p>Add Card</p>
|
||||
</div>
|
||||
<div className="m-1">
|
||||
<ModeToggle />
|
||||
</div>
|
||||
{session ? (
|
||||
<div className="m-1">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src="https://github.com/shadcn.png"
|
||||
alt="@shadcn"
|
||||
/>
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel>
|
||||
Hello {user?.username}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{/* <DropdownMenuItem>Profile</DropdownMenuItem>
|
||||
<DropdownMenuItem>Billing</DropdownMenuItem>
|
||||
<DropdownMenuItem>Team</DropdownMenuItem>
|
||||
<DropdownMenuItem>Subscription</DropdownMenuItem>
|
||||
<hr className="solid"></hr>
|
||||
<DropdownMenuItem>
|
||||
<div className="m-auto mt-3">
|
||||
<Button onClick={() => logout()} variant="ghost">
|
||||
Logout
|
||||
</Button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Link to="/login">Login</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
<SidebarProvider defaultOpen={sidebarState}>
|
||||
<AppSidebar />
|
||||
<Toaster expand={true} richColors closeButton />
|
||||
<Outlet />
|
||||
</SidebarProvider>
|
||||
</ThemeProvider>
|
||||
</SessionProvider>
|
||||
<DropdownMenuItem>Subscription</DropdownMenuItem> */}
|
||||
<hr className="solid"></hr>
|
||||
<DropdownMenuItem>
|
||||
<div className="m-auto">
|
||||
<button onClick={() => logout()}>Logout</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Link to="/login">Login</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
<SidebarProvider defaultOpen={sidebarState}>
|
||||
<AppSidebar />
|
||||
<Toaster expand={true} richColors closeButton />
|
||||
<Outlet />
|
||||
</SidebarProvider>
|
||||
</ThemeProvider>
|
||||
</SessionProvider>
|
||||
|
||||
<TanStackRouterDevtools position="bottom-right" />
|
||||
</>
|
||||
);
|
||||
},
|
||||
{/* <TanStackRouterDevtools position="bottom-right" /> */}
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
14
frontend/src/routes/_admin/servers.tsx
Normal file
14
frontend/src/routes/_admin/servers.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import ServerPage from "@/components/admin/servers/ServerPage";
|
||||
import {createFileRoute} from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/_admin/servers")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div>
|
||||
<ServerPage />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/ocp/lots')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/ocp/lots"!</div>
|
||||
}
|
||||
19
frontend/src/types/lots.ts
Normal file
19
frontend/src/types/lots.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export type LotType = {
|
||||
AV: number;
|
||||
Alias: string;
|
||||
LOT: number;
|
||||
LabelOnlineID: number;
|
||||
MachineDescription: string;
|
||||
MachineID: number;
|
||||
MachineLocation: number;
|
||||
PlannedQTY: number;
|
||||
PrinterName: string;
|
||||
Produced: number;
|
||||
ProlinkLot: number;
|
||||
Remaining: number;
|
||||
machineID: number;
|
||||
overPrinting: string;
|
||||
pallerCopies: number;
|
||||
palletLabel: string;
|
||||
printerID: number;
|
||||
};
|
||||
@@ -6,4 +6,5 @@ export type User = {
|
||||
username: string;
|
||||
roles: Roles[];
|
||||
role: string;
|
||||
prod?: string;
|
||||
};
|
||||
|
||||
8
frontend/src/utils/adminUrlCheck.ts
Normal file
8
frontend/src/utils/adminUrlCheck.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const adminUrlCheck = () => {
|
||||
const host = window.location.host.split(":")[0];
|
||||
const okHost = ["localhost", "usmcd1vms036"];
|
||||
if (okHost.includes(host)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
20
frontend/src/utils/querys/production/labels.tsx
Normal file
20
frontend/src/utils/querys/production/labels.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import {queryOptions} from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export function getlabels(hours: string) {
|
||||
return queryOptions({
|
||||
queryKey: ["labels"],
|
||||
queryFn: () => fetchSettings(hours),
|
||||
|
||||
staleTime: 1000,
|
||||
//refetchInterval: 2500,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
}
|
||||
|
||||
const fetchSettings = async (hours: string) => {
|
||||
const {data} = await axios.get(`/api/v1/ocp/labels?hours=${hours}`);
|
||||
// if we are not localhost ignore the devDir setting.
|
||||
//const url: string = window.location.host.split(":")[0];
|
||||
return data.data ?? [];
|
||||
};
|
||||
21
frontend/src/utils/querys/production/lots.tsx
Normal file
21
frontend/src/utils/querys/production/lots.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import {queryOptions} from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export function getlots() {
|
||||
return queryOptions({
|
||||
queryKey: ["lots"],
|
||||
queryFn: () => fetchSettings(),
|
||||
|
||||
staleTime: 10 * 1000,
|
||||
//refetchInterval: 10 * 1000,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
}
|
||||
|
||||
const fetchSettings = async () => {
|
||||
const {data} = await axios.get("/api/v1/ocp/lots");
|
||||
// if we are not localhost ignore the devDir setting.
|
||||
//const url: string = window.location.host.split(":")[0];
|
||||
let lotData = data.data;
|
||||
return lotData ?? [];
|
||||
};
|
||||
19
frontend/src/utils/querys/servers.tsx
Normal file
19
frontend/src/utils/querys/servers.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import {queryOptions} from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export function getServers(token: string) {
|
||||
return queryOptions({
|
||||
queryKey: ["servers"],
|
||||
queryFn: () => fetchSettings(token),
|
||||
enabled: !!token,
|
||||
staleTime: 1000,
|
||||
refetchInterval: 2500,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
}
|
||||
|
||||
const fetchSettings = async (token: string) => {
|
||||
const {data} = await axios.get("/api/server/servers", {headers: {Authorization: `Bearer ${token}`}});
|
||||
|
||||
return data.data;
|
||||
};
|
||||
@@ -13,6 +13,11 @@ export function getSettings(token: string) {
|
||||
|
||||
const fetchSettings = async (token: string) => {
|
||||
const {data} = await axios.get("/api/server/settings", {headers: {Authorization: `Bearer ${token}`}});
|
||||
|
||||
return data.data;
|
||||
// if we are not localhost ignore the devDir setting.
|
||||
const url: string = window.location.host.split(":")[0];
|
||||
let settingsData = data.data;
|
||||
if (url != "localhost") {
|
||||
settingsData.filter((n: any) => n.name === "devDir");
|
||||
}
|
||||
return settingsData;
|
||||
};
|
||||
|
||||
@@ -13,14 +13,13 @@ export function hasAccess(user: User | null, moduleName: string | null, modules:
|
||||
}
|
||||
|
||||
export function hasPageAccess(user: User | null, role: any, module_id: string): boolean {
|
||||
if (role.includes("viewer")) return true;
|
||||
if (!user) return false;
|
||||
|
||||
// get only the module in the user profile
|
||||
const userRole = user?.roles.filter((role) => role.module_id === module_id);
|
||||
|
||||
if (role.includes(userRole[0]?.role)) {
|
||||
return true;
|
||||
}
|
||||
if (role.includes(userRole[0]?.role)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,16 @@ export default defineConfig({
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
"/api": {target: `http://localhost:${Number(process.env.VITE_SERVER_PORT || 4400)}`, changeOrigin: true},
|
||||
"/api": {
|
||||
target: `http://localhost:${Number(process.env.VITE_SERVER_PORT || 4400)}`,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
"/ocme": {
|
||||
target: `http://localhost:${Number(process.env.VITE_SERVER_PORT || 4400)}`,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
20231
package-lock.json
generated
20231
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
144
package.json
144
package.json
@@ -1,67 +1,81 @@
|
||||
{
|
||||
"name": "fullstack-app",
|
||||
"version": "2.4.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
|
||||
"dev:server": "dotenvx run -f .env -- tsx watch server/index.ts",
|
||||
"dev:frontend": "cd frontend && npm run dev",
|
||||
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
|
||||
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
|
||||
"build": "npm run build:server && npm run build:frontend",
|
||||
"build:server": "rimraf dist && tsc --build && xcopy server\\scripts dist\\server\\scripts /E /I /Y",
|
||||
"build:frontend": "cd frontend && npm run build",
|
||||
"start": "set NODE_ENV=production && npm run start:server",
|
||||
"start:server": "dotenvx run -f .env -- node dist/server/index.js",
|
||||
"db:generate": "npx drizzle-kit generate",
|
||||
"db:migrate": "npx drizzle-kit push",
|
||||
"deploy": "standard-version --conventional-commits",
|
||||
"commit": "cz",
|
||||
"prodinstall": "npm i --omit=dev && npm run db:migrate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dotenvx/dotenvx": "^1.38.3",
|
||||
"@hono/node-server": "^1.13.8",
|
||||
"@hono/zod-openapi": "^0.18.4",
|
||||
"@scalar/hono-api-reference": "^0.5.175",
|
||||
"@types/jsonwebtoken": "^9.0.8",
|
||||
"axios": "^1.7.9",
|
||||
"bcrypt": "^5.1.1",
|
||||
"compression": "^1.8.0",
|
||||
"cookie": "^1.0.2",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"drizzle-kit": "^0.30.4",
|
||||
"drizzle-orm": "^0.39.3",
|
||||
"drizzle-zod": "^0.7.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mssql": "^11.0.1",
|
||||
"nodemailer": "^6.10.0",
|
||||
"nodemailer-express-handlebars": "^7.0.0",
|
||||
"pg": "^8.13.3",
|
||||
"pino": "^9.6.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"postgres": "^3.4.5",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/mssql": "^9.1.7",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/pg": "^8.11.11",
|
||||
"concurrently": "^8.2.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"standard-version": "^9.5.0",
|
||||
"tsx": "^4.7.1",
|
||||
"typescript": "~5.7.3"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
"name": "lstv2",
|
||||
"version": "2.9.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
|
||||
"dev:server": "dotenvx run -f .env -- tsx watch server/index.ts",
|
||||
"dev:frontend": "cd frontend && npm run dev",
|
||||
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
|
||||
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
|
||||
"build": "npm run build:server && npm run build:frontend",
|
||||
"build:server": "rimraf dist && tsc --build && npm run copy:scripts",
|
||||
"build:frontend": "cd frontend && npm run build",
|
||||
"copy:scripts": "tsx server/scripts/copyScripts.ts",
|
||||
"copy:servers": "xcopy server\\services\\server\\utils\\serverData.json dist\\server\\services\\server\\utils /E /I /Y",
|
||||
"start": "set NODE_ENV=production && npm run start:server",
|
||||
"start:server": "dotenvx run -f .env -- node dist/server/index.js",
|
||||
"db:generate": "npx drizzle-kit generate",
|
||||
"db:migrate": "npx drizzle-kit push",
|
||||
"db:dev": "npm run build && npm run db:generate && npm run db:migrate",
|
||||
"deploy": "standard-version --conventional-commits && npm run prodBuild",
|
||||
"zipServer": "dotenvx run -f .env -- tsx server/scripts/zipUpBuild.ts \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
||||
"v1Build": "cd C:\\Users\\matthes01\\Documents\\logisticsSupportTool && npm run oldBuilder",
|
||||
"prodBuild": "npm run v1Build && powershell -ExecutionPolicy Bypass -File server/scripts/build.ps1 -dir \"C:\\Users\\matthes01\\Documents\\lstv2\" && npm run zipServer",
|
||||
"commit": "cz",
|
||||
"prodinstall": "npm i --omit=dev && npm run db:migrate",
|
||||
"checkupdates": "npx npm-check-updates"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
},
|
||||
"admConfig": {
|
||||
"build": 50,
|
||||
"oldBuild": "backend-0.1.3.zip"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/mssql": "^9.1.7",
|
||||
"@types/node": "^22.13.11",
|
||||
"@types/pg": "^8.11.11",
|
||||
"@types/ws": "^8.18.0",
|
||||
"concurrently": "^9.1.2",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"drizzle-kit": "^0.30.5",
|
||||
"fs-extra": "^11.3.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"tsx": "^4.19.3",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dotenvx/dotenvx": "^1.39.0",
|
||||
"@hono/node-server": "^1.14.0",
|
||||
"@hono/zod-openapi": "^0.19.2",
|
||||
"@scalar/hono-api-reference": "^0.7.2",
|
||||
"@types/jsonwebtoken": "^9.0.9",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.8.4",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"date-fns": "^4.1.0",
|
||||
"drizzle-orm": "^0.41.0",
|
||||
"drizzle-zod": "^0.7.0",
|
||||
"fast-xml-parser": "^5.0.9",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mssql": "^11.0.1",
|
||||
"nodemailer": "^6.10.0",
|
||||
"nodemailer-express-handlebars": "^7.0.0",
|
||||
"pg": "^8.14.1",
|
||||
"pino": "^9.6.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"postgres": "^3.4.5",
|
||||
"rimraf": "^6.0.1",
|
||||
"ws": "^8.18.1",
|
||||
"zod": "^3.24.2"
|
||||
}
|
||||
}
|
||||
15
server/globalUtils/appInfo.ts
Normal file
15
server/globalUtils/appInfo.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import path from "path";
|
||||
import {createLog} from "../services/logger/logger.js";
|
||||
import fs from "fs";
|
||||
|
||||
export const getAppInfo = async (appLock: string) => {
|
||||
try {
|
||||
const packagePath = path.join(appLock, "package.json");
|
||||
const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
||||
//const version = packageJson.version;
|
||||
return packageJson;
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "zipUpBuild", `Error in getting the version: ${error}`);
|
||||
return error;
|
||||
}
|
||||
};
|
||||
3
server/globalUtils/delay.ts
Normal file
3
server/globalUtils/delay.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const delay = (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
8
server/globalUtils/routeDefs/options.ts
Normal file
8
server/globalUtils/routeDefs/options.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const apiOptions = () => {
|
||||
return {
|
||||
tags: ["rfid"],
|
||||
summary: "Add new reader",
|
||||
method: "post",
|
||||
path: "/addreader",
|
||||
};
|
||||
};
|
||||
45
server/globalUtils/routeDefs/responses.ts
Normal file
45
server/globalUtils/routeDefs/responses.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {z} from "@hono/zod-openapi";
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().openapi({example: true}),
|
||||
message: z.string().optional(),
|
||||
data: z
|
||||
.array(z.object({}).optional())
|
||||
.optional()
|
||||
.openapi({example: [{data: "hi"}]}),
|
||||
});
|
||||
|
||||
export const responses = () => {
|
||||
return {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {schema: responseSchema},
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Unauthenticated"})}),
|
||||
},
|
||||
},
|
||||
description: "Unauthorized",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
};
|
||||
};
|
||||
29
server/globalUtils/routeDefs/returnRes.ts
Normal file
29
server/globalUtils/routeDefs/returnRes.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { Context } from "hono";
|
||||
|
||||
export type ReturnRes<T> =
|
||||
| { success: true; message: string; data: T }
|
||||
| { success: false; message: string; error: any };
|
||||
|
||||
export const returnRes = <T>(
|
||||
success: boolean,
|
||||
message: string,
|
||||
data: T | null = null
|
||||
): ReturnRes<T> => {
|
||||
/**
|
||||
* just a simple return to reduce the typing and make sure we are always consitant with our returns.
|
||||
*
|
||||
* data can be an error as well.
|
||||
*/
|
||||
return success
|
||||
? { success, message, data: data as T }
|
||||
: { success, message, error: data ?? "An unknown error occurred" };
|
||||
};
|
||||
|
||||
// export const returnApi = (c:Context,success: boolean, message: string, data?: any, code: number)=>{
|
||||
// /**
|
||||
// * just a simple return to reduce the typing and make sure we are always consitant with our returns.
|
||||
// *
|
||||
// * data can be an error as well.
|
||||
// */
|
||||
// return c.json({success, message, data}, code);
|
||||
// }
|
||||
24
server/globalUtils/tryCatch.ts
Normal file
24
server/globalUtils/tryCatch.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// Types for the result object with discriminated union
|
||||
type Success<T> = {
|
||||
data: T;
|
||||
error: null;
|
||||
};
|
||||
|
||||
type Failure<E> = {
|
||||
data: null;
|
||||
error: E;
|
||||
};
|
||||
|
||||
type Result<T, E = Error> = Success<T> | Failure<E>;
|
||||
|
||||
// Main wrapper function
|
||||
export async function tryCatch<T, E = Error>(
|
||||
promise: Promise<T>
|
||||
): Promise<Result<T, E>> {
|
||||
try {
|
||||
const data = await promise;
|
||||
return { data, error: null };
|
||||
} catch (error) {
|
||||
return { data: null, error: error as E };
|
||||
}
|
||||
}
|
||||
193
server/index.ts
193
server/index.ts
@@ -1,12 +1,12 @@
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
import {serve} from "@hono/node-server";
|
||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {serveStatic} from "@hono/node-server/serve-static";
|
||||
import {logger} from "hono/logger";
|
||||
import {cors} from "hono/cors";
|
||||
import {createLog} from "./services/logger/logger.js";
|
||||
import {closePool} from "./services/sqlServer/prodSqlServer.js";
|
||||
|
||||
import { serve } from "@hono/node-server";
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { proxy } from "hono/proxy";
|
||||
import { serveStatic } from "@hono/node-server/serve-static";
|
||||
import { logger } from "hono/logger";
|
||||
import { cors } from "hono/cors";
|
||||
import { createLog } from "./services/logger/logger.js";
|
||||
import { WebSocketServer } from "ws";
|
||||
// custom routes
|
||||
import scalar from "./services/general/route/scalar.js";
|
||||
import system from "./services/server/systemServer.js";
|
||||
@@ -16,10 +16,12 @@ import ocme from "./services/ocme/ocmeService.js";
|
||||
import sqlService from "./services/sqlServer/sqlService.js";
|
||||
import logistics from "./services/logistics/logisticsService.js";
|
||||
import rfid from "./services/rfid/rfidService.js";
|
||||
|
||||
import {db} from "../database/dbclient.js";
|
||||
import {settings} from "../database/schema/settings.js";
|
||||
import {count, eq} from "drizzle-orm";
|
||||
import printers from "./services/printers/printerService.js";
|
||||
import loggerService from "./services/logger/loggerService.js";
|
||||
import ocpService from "./services/ocp/ocpService.js";
|
||||
import { db } from "../database/dbclient.js";
|
||||
import { settings } from "../database/schema/settings.js";
|
||||
import { count } from "drizzle-orm";
|
||||
|
||||
// create the main prodlogin here
|
||||
const username = "lst_user";
|
||||
@@ -27,115 +29,148 @@ const password = "Alpla$$Prod";
|
||||
export const lstAuth = btoa(`${username}:${password}`);
|
||||
|
||||
// checking to make sure we have the settings intialized
|
||||
const serverIntialized = await db.select({count: count()}).from(settings);
|
||||
export const installed = serverIntialized[0].count === 0 && process.env.NODE_ENV !== "development" ? false : true;
|
||||
const serverIntialized = await db.select({ count: count() }).from(settings);
|
||||
export const installed =
|
||||
serverIntialized[0].count === 0 && process.env.NODE_ENV !== "development"
|
||||
? false
|
||||
: true;
|
||||
createLog("info", "LST", "server", `Server is installed: ${installed}`);
|
||||
|
||||
const allowedOrigins = [
|
||||
"http://localhost:3000",
|
||||
"http://localhost:4000",
|
||||
"http://localhost:5173",
|
||||
`http://usmcd1vms006:4000`,
|
||||
];
|
||||
const app = new OpenAPIHono();
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
// middle ware
|
||||
app.use("*", logger());
|
||||
app.use(
|
||||
"*",
|
||||
cors({
|
||||
origin: allowedOrigins,
|
||||
allowHeaders: ["X-Custom-Header", "Upgrade-Insecure-Requests"],
|
||||
allowMethods: ["POST", "GET", "OPTIONS"],
|
||||
exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
|
||||
maxAge: 600,
|
||||
credentials: true,
|
||||
})
|
||||
"*",
|
||||
cors({
|
||||
origin: "*", // Allow all origins
|
||||
allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
|
||||
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
||||
//exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
|
||||
credentials: true, // Allow credentials if needed
|
||||
maxAge: 600,
|
||||
})
|
||||
);
|
||||
|
||||
// Middleware to normalize route case
|
||||
app.use("*", async (c, next) => {
|
||||
const lowercasedUrl = c.req.url.toLowerCase();
|
||||
const lowercasedUrl = c.req.url.toLowerCase();
|
||||
//console.log("Incoming Request:", c.req.url, c.req.method);
|
||||
// If the URL is already lowercase, continue as usual
|
||||
if (c.req.url === lowercasedUrl) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// If the URL is already lowercase, continue as usual
|
||||
if (c.req.url === lowercasedUrl) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Otherwise, re-route internally
|
||||
return c.redirect(lowercasedUrl, 308); // 308 preserves the HTTP method
|
||||
// Otherwise, re-route internally
|
||||
return c.redirect(lowercasedUrl, 308); // 308 preserves the HTTP method
|
||||
});
|
||||
|
||||
app.doc("/api/ref", {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
version: "2.0.0",
|
||||
title: "LST API",
|
||||
},
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
version: "2.0.0",
|
||||
title: "LST API",
|
||||
},
|
||||
});
|
||||
|
||||
const routes = [
|
||||
scalar,
|
||||
auth,
|
||||
// apiHits,
|
||||
system,
|
||||
tcpServer,
|
||||
sqlService,
|
||||
logistics,
|
||||
rfid,
|
||||
scalar,
|
||||
auth,
|
||||
// apiHits,
|
||||
system,
|
||||
tcpServer,
|
||||
sqlService,
|
||||
logistics,
|
||||
rfid,
|
||||
printers,
|
||||
loggerService,
|
||||
ocpService,
|
||||
] as const;
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/api/", route);
|
||||
app.route("/api/", route);
|
||||
});
|
||||
|
||||
// the catch all api route
|
||||
app.all("/api/*", (c) => c.json({error: "API route not found"}, 404));
|
||||
|
||||
app.route("/ocme/", ocme);
|
||||
|
||||
// async (c) => {
|
||||
// //return ocmeService(c);
|
||||
// c.json({error: "Ocme route not found"}, 404);
|
||||
//--------------- lst v1 proxy ----------------------\\
|
||||
// app.all("/api/v1/*", (c) => {
|
||||
// const path = c.req.path.replace("/api/v1/", ""); // Extract the subpath
|
||||
// const query = c.req.query() ? "?" + new URLSearchParams(c.req.query()).toString() : ""; // Get query params
|
||||
// return proxy(`http://localhost:4900/${path}${query}`, {
|
||||
// headers: {
|
||||
// ...c.req.header(),
|
||||
// "X-Forwarded-For": "127.0.0.1",
|
||||
// "X-Forwarded-Host": c.req.header("host"),
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
|
||||
// app.all("/system/*", (c) => {
|
||||
// const path = c.req.path.replace("/system/", ""); // Extract the subpath
|
||||
// const query = c.req.query() ? "?" + new URLSearchParams(c.req.query()).toString() : ""; // Get query params
|
||||
// return proxy(`http://localhost:4200/${path}${query}`, {
|
||||
// headers: {
|
||||
// ...c.req.header(),
|
||||
// "X-Forwarded-For": "127.0.0.1",
|
||||
// "X-Forwarded-Host": c.req.header("host"),
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
|
||||
//---------------------------------------------------\\
|
||||
|
||||
// the catch all api route
|
||||
app.all("/api/*", (c) => c.json({ error: "API route not found" }, 404));
|
||||
|
||||
// front end static files
|
||||
app.use("/*", serveStatic({root: "./frontend/dist"}));
|
||||
app.use("*", serveStatic({path: "./frontend/dist/index.html"}));
|
||||
app.use("/*", serveStatic({ root: "./frontend/dist" }));
|
||||
app.use("*", serveStatic({ path: "./frontend/dist/index.html" }));
|
||||
|
||||
// Handle app exit signals
|
||||
process.on("SIGINT", async () => {
|
||||
console.log("\nGracefully shutting down...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
console.log("\nGracefully shutting down...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
console.log("Received termination signal, closing database...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
console.log("Received termination signal, closing database...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on("uncaughtException", async (err) => {
|
||||
console.log("Uncaught Exception:", err);
|
||||
//await closePool();
|
||||
process.exit(1);
|
||||
console.log("Uncaught Exception:", err);
|
||||
//await closePool();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on("beforeExit", async () => {
|
||||
console.log("Process is about to exit...");
|
||||
//await closePool();
|
||||
console.log("Process is about to exit...");
|
||||
//await closePool();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
const port =
|
||||
process.env.NODE_ENV === "development"
|
||||
? process.env.VITE_SERVER_PORT
|
||||
: process.env.PROD_PORT;
|
||||
|
||||
serve(
|
||||
{
|
||||
fetch: app.fetch,
|
||||
port: Number(process.env.VITE_SERVER_PORT),
|
||||
hostname: "0.0.0.0",
|
||||
},
|
||||
(info) => {
|
||||
createLog("info", "LST", "server", `Server is running on http://${info.address}:${info.port}`);
|
||||
}
|
||||
{
|
||||
fetch: app.fetch,
|
||||
port: Number(port),
|
||||
hostname: "0.0.0.0",
|
||||
},
|
||||
(info) => {
|
||||
createLog(
|
||||
"info",
|
||||
"LST",
|
||||
"server",
|
||||
`Server is running on http://${info.address}:${info.port}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export type AppRoutes = typeof appRoutes;
|
||||
|
||||
@@ -2,6 +2,7 @@ param (
|
||||
[string]$dir,
|
||||
[string]$app
|
||||
)
|
||||
# dir is the location of the root folder.
|
||||
|
||||
# Store the original directory
|
||||
$originalDir = Get-Location
|
||||
@@ -23,38 +24,12 @@ if (-not (Test-Path $dir)) {
|
||||
# Navigate to the directory
|
||||
Set-Location -Path $dir
|
||||
|
||||
Write-Host "Cleaning the app."
|
||||
# Run npm run build
|
||||
Write-Host "Running 'npm run build' in directory: $dir"
|
||||
npm run build
|
||||
|
||||
# Function to delete all `dist` and `.turbo` folders recursively
|
||||
function Delete-Folders {
|
||||
param (
|
||||
[string]$folderName
|
||||
)
|
||||
Write-Host "Build completed successfully."
|
||||
|
||||
# Define the directories to search (packages and apps)
|
||||
$searchDirectories = @("/", "frontend")
|
||||
|
||||
foreach ($searchDir in $searchDirectories) {
|
||||
$fullSearchPath = Join-Path -Path $dir -ChildPath $searchDir
|
||||
|
||||
# Check if the directory exists
|
||||
if (Test-Path $fullSearchPath) {
|
||||
# Find all folders matching the name
|
||||
$folders = Get-ChildItem -Path $fullSearchPath -Recurse -Directory -Filter $folderName -ErrorAction SilentlyContinue
|
||||
|
||||
if ($folders) {
|
||||
#Write-Host "Deleting all '$folderName' folders in $fullSearchPath and its subdirectories..."
|
||||
foreach ($folder in $folders) {
|
||||
#Write-Host "Deleting: $($folder.FullName)"
|
||||
Remove-Item -Path $folder.FullName -Recurse -Force
|
||||
}
|
||||
} else {
|
||||
# Write-Host "No '$folderName' folders found in $fullSearchPath and its subdirectories."
|
||||
}
|
||||
} else {
|
||||
# Write-Host "Directory '$fullSearchPath' does not exist."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Delete-Folders -folderName "dist"
|
||||
# Restore the original directory
|
||||
Set-Location -Path $originalDir
|
||||
exit 0
|
||||
65
server/scripts/copyScripts.ts
Normal file
65
server/scripts/copyScripts.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
// Get the current directory of the module
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const sourceDir = path.join(__dirname, "/");
|
||||
const destDir = path.join("./", "dist", "server", "scripts");
|
||||
|
||||
// Copy only .ps1 files
|
||||
fs.readdir(sourceDir)
|
||||
.then((files) => {
|
||||
files.forEach((file) => {
|
||||
if (path.extname(file) === ".ps1") {
|
||||
const sourceFile = path.join(sourceDir, file);
|
||||
const destFile = path.join(destDir, file);
|
||||
|
||||
// Copy each .ps1 file
|
||||
fs.copy(sourceFile, destFile)
|
||||
.then(() => {
|
||||
console.log(`Copied: ${file}`);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Error copying file: ${file}`, err);
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error reading source directory:", err);
|
||||
});
|
||||
|
||||
// Paths for source and destination of serverData.json
|
||||
const sourceFile = path.join(
|
||||
"./",
|
||||
"server",
|
||||
"services",
|
||||
"server",
|
||||
"utils",
|
||||
"serverData.json"
|
||||
);
|
||||
const serverDataDest = path.join(
|
||||
"./",
|
||||
"dist",
|
||||
"server",
|
||||
"services",
|
||||
"server",
|
||||
"utils"
|
||||
);
|
||||
const destFile = path.join(serverDataDest, "serverData.json");
|
||||
|
||||
// Ensure the destination directory exists
|
||||
fs.ensureDir(destDir)
|
||||
.then(() => {
|
||||
// Copy the serverData.json file
|
||||
return fs.copy(sourceFile, destFile);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("serverData.json copied successfully!");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error copying serverData.json:", err);
|
||||
});
|
||||
@@ -1,37 +1,56 @@
|
||||
# Install
|
||||
|
||||
## Files needed to be downloaded before install.
|
||||
|
||||
### To run the server
|
||||
* [PostgresSQL](https://www.postgresql.org/download/windows/) - current version using is 17
|
||||
* [NodeJS](https://nodejs.org)
|
||||
* [NSSM](https://nssm.cc/)
|
||||
|
||||
- [PostgresSQL](https://www.postgresql.org/download/windows/) - current version using is 17
|
||||
- [NodeJS](https://nodejs.org)
|
||||
- [NSSM](https://nssm.cc/)
|
||||
|
||||
### To manage the server
|
||||
* [VSCODE](https://code.visualstudio.com/)
|
||||
* [Postman](https://www.postman.com/downloads/)
|
||||
|
||||
- [VSCODE](https://code.visualstudio.com/)
|
||||
- [Postman](https://www.postman.com/downloads/)
|
||||
|
||||
## Creating directories needed
|
||||
* Create a new folder where we will host the server files.
|
||||
* Copy the nssm.exe into this folder
|
||||
* Copy the build files to the server (only needed for intial install).
|
||||
* This will house all the compiles and minified files needed to start the server up, this includes the frontend.
|
||||
* Save the nssm.exe into this folder as well, this will be used to control the service.
|
||||
|
||||
- Create a new folder where we will host the server files.
|
||||
- Copy the nssm.exe into this folder
|
||||
- Copy the build files to the server (only needed for intial install).
|
||||
- This will house all the compiles and minified files needed to start the server up, this includes the frontend.
|
||||
- Save the nssm.exe into this folder as well, this will be used to control the service.
|
||||
|
||||
## Do the intial install
|
||||
|
||||
### DB instal setup
|
||||
|
||||
1. Install postgres
|
||||
2. Open pgAdmin
|
||||
3. create a new Database named lst_db
|
||||
|
||||
### Intial server setup
|
||||
|
||||
1. Open VSCode and navigate to the folder where you extracted the files.
|
||||
2. Click trusted when it pops up.
|
||||
3. Open a terminal window inside vscode.
|
||||
4. Run the install script this will install all dependaceys needed as well as do all the database migrations
|
||||
|
||||
```bash
|
||||
npm run prodinstall
|
||||
npm run prodinstall
|
||||
```
|
||||
|
||||
Next we want to do an intial build for the db
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Create the .env file
|
||||
|
||||
In the root of the folder create a new .env file
|
||||
add in the below and change each setting area that says change me to something that suits your needs
|
||||
|
||||
```env
|
||||
# PORTS
|
||||
# To keep it all simple we will pass VITE to the ports that are used on both sides.
|
||||
@@ -39,7 +58,9 @@ VITE_SERVER_PORT=4400
|
||||
|
||||
# logLevel
|
||||
LOG_LEVEL=debug
|
||||
|
||||
PROD_PORT=4000
|
||||
# DUE to lstv1 we need 3000
|
||||
SEC_PORT=3000
|
||||
# Auth stuff
|
||||
SALTING=12
|
||||
SECRET=CHANGEME
|
||||
@@ -47,13 +68,13 @@ JWT_SECRET=CHANGEME
|
||||
JWT_REFRESH_SECRET=CHANGEME
|
||||
|
||||
# Expire info plus refresh change as needed
|
||||
JWT_EXPIRES=60
|
||||
JWT_EXPIRES=60
|
||||
JWT_REFRESH_THRESHOLD=30
|
||||
JWT_ACCESS_EXPIRATION="1h"
|
||||
JWT_REFRESH_EXPIRATION="7d"
|
||||
|
||||
# this code will need to be used when a user needs to have access to everything.
|
||||
SECRETOVERRIDECODE="mVSDCpBdxreIJ979ziI71GRubBc2mqVqvZdfA22CB7smBfqlE9S3rKTE909yCHte"
|
||||
SECRETOVERRIDECODE="supersecretKey"
|
||||
|
||||
# Database url - please change the password if this is all you changed
|
||||
DATABASE_URL="postgresql://postgres:PASSWORD@localhost:5432/lst_db"
|
||||
@@ -64,62 +85,80 @@ MAXLOTS=3
|
||||
```
|
||||
|
||||
### Run the start command to get all the basic settings and modules installed
|
||||
|
||||
1. Run the below
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
This command will start up the server and seed the database.
|
||||
* Settings will be set here.
|
||||
* All modules will be added.
|
||||
|
||||
- Settings will be set here.
|
||||
- All modules will be added.
|
||||
|
||||
2. Press CTRL + C to stop the server.
|
||||
3. Reopen postgres and review the settings make the changes to match the server your going to be running in.
|
||||
* Change the server
|
||||
* change the dbServer
|
||||
* change plantToken
|
||||
* then the remaining settings confirm if you need on or want to leave as default.
|
||||
- Change the server
|
||||
- change the dbServer
|
||||
- change plantToken
|
||||
- then the remaining settings confirm if you need on or want to leave as default.
|
||||
|
||||
### Creating first user.
|
||||
|
||||
1. Start the server back up.
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
2. Open http://[SERVER]:[PORT]/api/docs or postman and create a user.
|
||||
* Please do not try to manually enter a new user this is due to how the password is hashed, as well as setting systemAdmin for the first user.
|
||||
* Change the server and port to what you changed in the DB.
|
||||
- Please do not try to manually enter a new user this is due to how the password is hashed, as well as setting systemAdmin for the first user.
|
||||
- Change the server and port to what you changed in the DB.
|
||||
3. Stop the server again with CTRL + C.
|
||||
|
||||
### Running as a serivice.
|
||||
|
||||
You want to CD into the scripts folder.
|
||||
|
||||
```bash
|
||||
cd .\dist\server\scripts\
|
||||
```
|
||||
|
||||
Next use the example command below to get the service up and running.
|
||||
* Options legend
|
||||
* serviceName = not recommended to change to reduce issues with the update process
|
||||
* option = use install for the install, but you can use this script later to stop, start, restart the service.
|
||||
* appPath = where did you extract the server files
|
||||
* description = no need to change this unless you want it to be something else
|
||||
* command = do not change this unless you know what your doing and really need to change this.
|
||||
|
||||
- Options legend
|
||||
- serviceName = not recommended to change to reduce issues with the update process
|
||||
- option = use install for the install, but you can use this script later to stop, start, restart the service.
|
||||
- appPath = where did you extract the server files
|
||||
- description = no need to change this unless you want it to be something else
|
||||
- command = do not change this unless you know what your doing and really need to change this.
|
||||
|
||||
```powershell
|
||||
.\services.ps1 -serviceName "LSTV2" -option "install" -appPath "E:\LST\lstV2" -description "Logistics Support Tool V2" -command "run start"
|
||||
```
|
||||
|
||||
# Migrating From V1 to V2
|
||||
|
||||
## User migration
|
||||
|
||||
1. Open the sqlite db and export to sql the users table
|
||||
2. OPen the sql in notepad++ or your editor of choice and change the query to be similar to below.
|
||||
* we only need to have save the username, role, email, password
|
||||
- we only need to have save the username, role, email, password
|
||||
|
||||
An example new query will look like
|
||||
* Below is how it looks when exported from sqlite
|
||||
|
||||
- Below is how it looks when exported from sqlite
|
||||
|
||||
```sql
|
||||
INSERT INTO "User" ("id", "username", "email", "role", "password", "passwordToken", "tokenExpire", "active", "pinCode", "lastLogin", "add_user", "add_date", "upd_user", "upd_date") VALUES
|
||||
(1, 'matthes01', 'blake.matthes@alpla.com', 'admin', 'JDJiJDEMUJEdGtL', NULL, NULL, '1', NULL, '1721075647687', 'LST_System', '1721075647687', 'LST_System', '1721075647687');
|
||||
```
|
||||
|
||||
The way we want to put recreate the query to work with the new db
|
||||
* Below example
|
||||
|
||||
- Below example
|
||||
|
||||
```sql
|
||||
INSERT INTO "users" ("username", "email", "role", "password") VALUES
|
||||
('matthes01','blake.matthes@alpla.com','admin','JDJiJDE1FuNFpkYlk4NGdHUXpEMzlHR1BD'),
|
||||
@@ -127,4 +166,12 @@ INSERT INTO "users" ("username", "email", "role", "password") VALUES
|
||||
('brandon001','brandon.harry@alpla.com','manager','wdm1RSXJlZnJDYTZP');
|
||||
;
|
||||
```
|
||||
* You could have many users and just add like above with the identical info from the db
|
||||
|
||||
- You could have many users and just add like above with the identical info from the db
|
||||
|
||||
## Running v1 along Side V2 for the interm
|
||||
|
||||
- change v2 prod port to 4000 in the env and db
|
||||
- change v1 env to 4400 in the env. and in the db you will need to change the auth server to 4000 and the serverPort to 4400
|
||||
|
||||
This will change so that v2 is the main server now, this is needed for ocme mainly.
|
||||
|
||||
@@ -1,14 +1,424 @@
|
||||
$serverDataFile = "..\services\server\utils\serverData.json"
|
||||
param (
|
||||
[string]$server,
|
||||
[string]$token,
|
||||
[string]$location,
|
||||
[string]$devFolder,
|
||||
[string]$serverIP,
|
||||
[string]$build,
|
||||
[string]$type,
|
||||
[string]$username,
|
||||
[string]$admpass,
|
||||
[string]$obslst,
|
||||
[string]$obsBuild
|
||||
)
|
||||
|
||||
# Convert the plain-text password to a SecureString
|
||||
$securePass = ConvertTo-SecureString $admpass -AsPlainText -Force
|
||||
$credentials = New-Object System.Management.Automation.PSCredential($username, $securePass)
|
||||
|
||||
# lets get the version of the app we are updating
|
||||
$pkgFile = "$devFolder\package.json"
|
||||
$package = Get-Content -Path $pkgFile -Raw | ConvertFrom-Json
|
||||
|
||||
$version = "$($package.version)-$($package.admConfig.build -1)"
|
||||
|
||||
# Checking to make sure the server is up and online
|
||||
Write-Output "Checking if $($token) is online to update."
|
||||
$pingResult = Test-Connection -ComputerName $serverIP -Count 2 -Quiet
|
||||
|
||||
if (-not $pingResult) {
|
||||
Write-Output "Server $($server) $($serverIP) is NOT reachable. Exiting script."
|
||||
exit 1 # Terminate the script with a non-zero exit code
|
||||
}
|
||||
|
||||
Write-Output "Server $($server) ($serverIP) is online."
|
||||
|
||||
# get the file name we want to grab
|
||||
|
||||
$buildZip = "lstv2-$version.zip"
|
||||
|
||||
if (-Not (Test-Path -Path "$($build)\$($buildZip)")) {
|
||||
Write-Host "Build is missing from the build folder."
|
||||
Write-host $buildZip
|
||||
exit
|
||||
}
|
||||
|
||||
Write-Host "---------------Starting the update Process----------------------------------"
|
||||
Write-Host "File to be copied over is $buildZip"
|
||||
Write-Host "Coping files to $($server)"
|
||||
$destination = "\\$($server)\$($location)" -replace ":", "$"
|
||||
Write-Host $destination
|
||||
Write-Host "Forcing the removal of the mapped drive."
|
||||
Get-PSDrive -Name "z" -ErrorAction SilentlyContinue | Remove-PSDrive -Force
|
||||
|
||||
# Create a mapped drive with credentials using New-PSDrive for the current session
|
||||
|
||||
try {
|
||||
|
||||
New-PSDrive -Name "z" -PSProvider FileSystem -Root $destination -Credential $credentials
|
||||
|
||||
# Create the update folder if it doesn't exist
|
||||
if (-not (Test-Path -Path $destination)) {
|
||||
New-Item -ItemType Directory -Path $destination -Force
|
||||
}
|
||||
|
||||
# Copying files to the server
|
||||
Write-Host "Copying files to $($server)"
|
||||
Copy-Item -Path "$($build)\$($buildZip)" -Destination "z:\" -Recurse -Force
|
||||
Write-Host "Files copied to $($server)"
|
||||
} catch {
|
||||
Write-Host "Error: $_"
|
||||
} finally {
|
||||
# Remove the mapped drive after copying
|
||||
if (Get-PSDrive -Name "z" -ErrorAction SilentlyContinue) {
|
||||
Write-Host "Removing mapped drive..."
|
||||
Remove-PSDrive -Name "z"
|
||||
}
|
||||
}
|
||||
|
||||
write-Host $extractedFolderPath = "$server\$location$(if ($token -eq "usiow2") { "_2" })"
|
||||
|
||||
# The script that runs inside the plant.
|
||||
$plantFunness = {
|
||||
param ($server, $token, $location, $buildFile, $buildLoc, $obslst, $obsBuild)
|
||||
|
||||
$localPath = $location -replace '\$', ':'
|
||||
$serverFile = "$($localPath)\$buildFile"
|
||||
$serverPath = "$($localPath)"
|
||||
|
||||
Write-Host "In the plant we go!!!!!"
|
||||
Write-Host "Unzipping the folder..."
|
||||
|
||||
$extractedFolderPath = $serverPath
|
||||
|
||||
# Extract the files to the build path
|
||||
try {
|
||||
# Expand the archive
|
||||
Expand-Archive -Path $serverFile -DestinationPath $extractedFolderPath -Force
|
||||
|
||||
# Delete the zip file after extraction
|
||||
Write-Host "Deleting the zip file..."
|
||||
Remove-Item -Path $serverFile -Force
|
||||
} catch {
|
||||
Write-Host "Error: $_"
|
||||
exit 1 # Exit with a non-zero code if there's an error
|
||||
}
|
||||
|
||||
Write-Host "-----------------------Dealing with LSTv1 Stuff ------------------------------------"
|
||||
try {
|
||||
# Expand the archive
|
||||
Expand-Archive -Path "$($localPath)\$($obsBuild)" -DestinationPath $obslst -Force
|
||||
|
||||
# Delete the zip file after extraction
|
||||
Write-Host "Deleting the zip file..."
|
||||
Remove-Item -Path "$($localPath)\$($obsBuild)" -Force
|
||||
} catch {
|
||||
Write-Host "Error: $_"
|
||||
exit 1 # Exit with a non-zero code if there's an error
|
||||
}
|
||||
|
||||
############################################################################
|
||||
Write-Host "Stopping the services to do the updates, pkgs and db changes."
|
||||
|
||||
Write-Host "Stopping services to update"
|
||||
$serviceGateway = "LST-Gateway$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceAuth = "LST-Auth$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceSystem = "LST-System$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceApp = "LST-App$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceFrontEnd = "LST-frontend$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceOcme = "LST-Ocme$(if ($token -eq "usiow2") { "_2" })"
|
||||
$serviceLstV2 = "LSTV2$(if ($token -eq "usiow2") { "_2" })"
|
||||
|
||||
if($token -eq "usday1"){
|
||||
Write-Host "Stopping $($serviceOcme)"
|
||||
Stop-Service -DisplayName $serviceOcme -Force
|
||||
}
|
||||
|
||||
Write-Host "Stopping $($serviceGateway)"
|
||||
Stop-Service -DisplayName $serviceGateway -Force
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
Write-Host "Stopping $($serviceAuth)"
|
||||
Stop-Service -DisplayName $serviceAuth -Force
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
Write-Host "Stopping $($serviceSystem)"
|
||||
Stop-Service -DisplayName $serviceSystem -Force
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
Write-Host "Stopping $($serviceApp)"
|
||||
Stop-Service -DisplayName $serviceApp -Force
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
Write-Host "Stopping $($serviceFrontEnd)"
|
||||
Stop-Service -DisplayName $serviceFrontEnd -Force
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
Write-Host "Stopping $($serviceLstV2)"
|
||||
Stop-Service -DisplayName $serviceLstV2 -Force
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
#################################################################
|
||||
# Service removoal and making sure we have the new version added
|
||||
#################################################################
|
||||
|
||||
$appPath = $extractedFolderPath
|
||||
$nssmPath = $serverPath + "\nssm.exe"
|
||||
$npmPath = "C:\Program Files\nodejs\npm.cmd" # Path to npm.cmd
|
||||
|
||||
#################################################################
|
||||
# Removing all the old services
|
||||
#################################################################
|
||||
Write-Host "Removing services that are no longer used."
|
||||
& $nssmPath remove "LogisticsSupportTool" confirm
|
||||
& $nssmPath remove $serviceAuth confirm
|
||||
# & $nssmPath remove $serviceGateway confirm
|
||||
# if($token -eq "usday1"){
|
||||
# & $nssmPath remove $serviceOcme confirm
|
||||
# }
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
$service = Get-Service -Name $serviceLstV2 -ErrorAction SilentlyContinue
|
||||
|
||||
if(-not $service){
|
||||
## adding in lstAdm
|
||||
Write-Host "Adding $($serviceLstV2)... incase its missing."
|
||||
$commandToRun = "run start"
|
||||
$description = "logistics Support Tool"
|
||||
& $nssmPath install $serviceLstV2 $npmPath $commandToRun
|
||||
Write-Host "Setting the app directory"
|
||||
& $nssmPath set $serviceLstV2 AppDirectory $appPath
|
||||
Write-Host "Setting the description"
|
||||
& $nssmPath set $serviceLstV2 Description $description
|
||||
Write-Host "Setting recovery options"
|
||||
# Set recovery options
|
||||
sc.exe failure $serviceLstV2 reset= 0 actions= restart/5000/restart/5000/restart/5000
|
||||
}
|
||||
# Doing an install
|
||||
Write-Host "Running the install to make sure everything is updated."
|
||||
Set-Location $serverPath
|
||||
npm run prodinstall # --omit=dev
|
||||
Write-Host "Finished doing updates"
|
||||
Start-Sleep -Seconds 1
|
||||
|
||||
###########################################################
|
||||
# Old system still active until we have everything off it
|
||||
###########################################################
|
||||
|
||||
###########################################################
|
||||
# Frontend env
|
||||
###########################################################
|
||||
|
||||
Write-Host "Creating the env file in the front end"
|
||||
$envContentTemplatef = @"
|
||||
NEXTAUTH_SECRET= "12348fssad5sdg2f2354afvfw34"
|
||||
NEXTAUTH_URL_INTERNAL= "http://localhost:3000"
|
||||
NEXTAUTH_URL="{url}"
|
||||
API_KEY= "E3ECD3619A943B98C6F33E3322362"
|
||||
"@
|
||||
|
||||
try {
|
||||
$url = "http://$($token)vms006:3000"
|
||||
|
||||
if ($token -eq "usiow2") {
|
||||
$url = "http://usiow1vms006:3001"
|
||||
}
|
||||
|
||||
if ($token -in @("test1", "test2", "test3")) {
|
||||
$url = "http://usmcd1vms036:3000"
|
||||
}
|
||||
|
||||
# Replace {url} with the actual $url
|
||||
$envContentf = $envContentTemplatef -replace "{url}", $url
|
||||
|
||||
# Define the path where the .env file should be created
|
||||
$envFilePathf = $obslst + "\apps\frontend\.env"
|
||||
Write-Host "Final URL: $url"
|
||||
# Write the content to the .env file
|
||||
$envContentf | Out-File -FilePath $envFilePathf -Encoding UTF8 -Force
|
||||
|
||||
# Optional: Verify the file was created
|
||||
if (Test-Path $envFilePathf) {
|
||||
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathf"
|
||||
} else {
|
||||
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||
}
|
||||
|
||||
} catch {
|
||||
Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# DB env
|
||||
###########################################################
|
||||
|
||||
Write-Host "Creating the env file in the front end"
|
||||
$envContentTemplateb = @"
|
||||
DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
||||
"@
|
||||
|
||||
try {
|
||||
|
||||
$dbLink = "lstBackendDB"
|
||||
|
||||
if ($token -eq "usiow2") {
|
||||
$dbLink = "lstBackendDB_2"
|
||||
}
|
||||
|
||||
if ($token -in @("test1", "test2", "test3")) {
|
||||
$dbLink = "lstBackendDB"
|
||||
}
|
||||
|
||||
# Replace {url} with the actual $url
|
||||
$envContentb = $envContentTemplateb -replace "{dbLink}", $dbLink
|
||||
|
||||
# Define the path where the .env file should be created
|
||||
$envFilePathb = $obslst + "\packages\database\.env"
|
||||
|
||||
# Write the content to the .env file
|
||||
$envContentb | Out-File -FilePath $envFilePathb -Encoding UTF8 -Force
|
||||
|
||||
# Optional: Verify the file was created
|
||||
if (Test-Path $envFilePathb) {
|
||||
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathb"
|
||||
} else {
|
||||
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||
}
|
||||
|
||||
} catch {
|
||||
Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# backend env
|
||||
###########################################################
|
||||
|
||||
Write-Host "Creating the env file in the front end"
|
||||
$envContentTemplated = @"
|
||||
# Server env
|
||||
NODE_ENV = production
|
||||
# server apiKey
|
||||
API_KEY = E3ECD3619A943B98C6F33E3322362
|
||||
# Prisma DB link
|
||||
DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
||||
# if you still want the db in the same folder as the server install you need to do like the example below else use the relevent link
|
||||
DATEBASE_LOC="E:\LST\db\{dbLink}.db"
|
||||
DATABASE_BACKUP_LOC="E:\LST\backups"
|
||||
# Server port
|
||||
GATEWAY_PORT={gatewayport}
|
||||
AUTH_PORT=4100
|
||||
SYSTEM_APP_PORT={systemport}
|
||||
OCME_PORT={ocme}
|
||||
|
||||
# This should me removed once we have the entire app broke out to its own apps
|
||||
OLD_APP_PORT={appPort}
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL = info
|
||||
LOG_LOC ="E:\\LST\\logs"
|
||||
|
||||
# authentication
|
||||
SALTING = 12
|
||||
SECRET = E3ECD3619A943B98C6F33E3322362
|
||||
JWT_SECRET = 12348fssad5sdg2f2354afvfw34
|
||||
JWT_EXPIRES_TIME = 1h
|
||||
|
||||
# cookie time is in min please take this into consideration when creating all the times
|
||||
COOKIE_EXPIRES_TIME = 60
|
||||
|
||||
# password token reset in mintues
|
||||
RESET_TOKEN = 330
|
||||
"@
|
||||
|
||||
try {
|
||||
|
||||
$dbLink = "lstBackendDB"
|
||||
$gatewayport = "4400"
|
||||
$systemport = "4200"
|
||||
$ocmeport = "4300"
|
||||
$appport = "4900"
|
||||
|
||||
if ($token -eq "usiow2") {
|
||||
$dbLink = "lstBackendDB_2"
|
||||
$gatewayport = "4401"
|
||||
$systemport = "4201"
|
||||
$ocmeport = "4301"
|
||||
$appport = "4901"
|
||||
}
|
||||
|
||||
if ($token -in @("test1", "test2", "test3")) {
|
||||
$dbLink = "lstBackendDB"
|
||||
}
|
||||
|
||||
#
|
||||
$port1 = $envContentTemplated -replace "{gatewayport}", $gatewayport
|
||||
$port2 = $port1 -replace "{systemport}", $systemport
|
||||
$port3 = $port2 -replace "{ocme}", $ocmeport
|
||||
$port4 = $port3 -replace "{appPort}", $appport
|
||||
$envContentd = $port4 -replace "{dbLink}", $dbLink
|
||||
|
||||
|
||||
$jsonContent = Get-Content -Path $serverDataFile -Raw
|
||||
|
||||
# Convert the JSON content to a PowerShell object
|
||||
$jsonObject = $jsonContent | ConvertFrom-Json
|
||||
|
||||
# Access the data in the JSON object
|
||||
$servers = $jsonObject.servers
|
||||
# Define the path where the .env file should be created
|
||||
$envFilePathd = $obslst + "\.env"
|
||||
|
||||
# Write the content to the .env file
|
||||
$envContentd | Out-File -FilePath $envFilePathd -Encoding UTF8 -Force
|
||||
|
||||
# Optional: Verify the file was created
|
||||
if (Test-Path $envFilePathd) {
|
||||
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathd"
|
||||
} else {
|
||||
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||
}
|
||||
|
||||
} catch {
|
||||
Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||
}
|
||||
|
||||
|
||||
Write-Host "Running install on obs server."
|
||||
Set-Location $obslst
|
||||
npm run newinstall # --omit=dev
|
||||
Write-Host "Update the frontend"
|
||||
npm run install:front
|
||||
npm run install:ui
|
||||
npm run install:db
|
||||
|
||||
Write-Host $servers
|
||||
Write-Host "Running db updates"
|
||||
npm run db:migrate
|
||||
Start-Sleep -Seconds 1
|
||||
npm run db:gen
|
||||
Start-Sleep -Seconds 1
|
||||
Write-Host "incase a new default setting was added we want to add it in."
|
||||
npm run db:init
|
||||
|
||||
###########################################################
|
||||
# Starting the services back up.
|
||||
###########################################################
|
||||
Write-Host "Starting the services"
|
||||
Write-Host "Starting $($serviceSystem)"
|
||||
Start-Service -DisplayName $serviceSystem
|
||||
Start-Sleep -Seconds 1
|
||||
Write-Host "Starting $($serviceGateway)"
|
||||
Start-Service -DisplayName $serviceGateway
|
||||
Start-Sleep -Seconds 1
|
||||
#Write-Host "Starting $($serviceAuth)"
|
||||
#Start-Service -DisplayName $serviceAuth
|
||||
#Start-Sleep -Seconds 1
|
||||
Write-Host "Starting $($serviceApp)"
|
||||
Start-Service -DisplayName $serviceApp
|
||||
Start-Sleep -Seconds 1
|
||||
Write-Host "Starting $($serviceFrontEnd)"
|
||||
Start-Service -DisplayName $serviceFrontEnd
|
||||
Start-Sleep -Seconds 1
|
||||
Write-Host "Starting $( $serviceLstV2)"
|
||||
Start-Service -DisplayName $serviceLstV2
|
||||
Start-Sleep -Seconds 1
|
||||
Write-Host "$($server) finished updating"
|
||||
if($token -eq "usday1"){
|
||||
Write-Host "Starting $($serviceOcme)"
|
||||
Start-Service -DisplayName $serviceOcme
|
||||
}
|
||||
|
||||
}
|
||||
Invoke-Command -ComputerName $server -ScriptBlock $plantFunness -ArgumentList $server, $token, $location, $buildZip, $buildLoc, $obslst, $obsBuild -Credential $credentials
|
||||
173
server/scripts/updateServers.ts
Normal file
173
server/scripts/updateServers.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import {spawn} from "child_process";
|
||||
import {getAppInfo} from "../globalUtils/appInfo.js";
|
||||
import {db} from "../../database/dbclient.js";
|
||||
import {serverData} from "../../database/schema/serverData.js";
|
||||
import {eq, sql} from "drizzle-orm";
|
||||
import {createLog} from "../services/logger/logger.js";
|
||||
|
||||
type UpdateServerResponse = {
|
||||
success: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export const updateServer = async (devApp: string, server: string | null): Promise<UpdateServerResponse> => {
|
||||
const app = await getAppInfo(devApp);
|
||||
const serverInfo = await db
|
||||
.select()
|
||||
.from(serverData)
|
||||
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
|
||||
|
||||
if (serverInfo.length === 0) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`Looks like you are missing the plant token or have entered an incorrect one please try again.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "Looks like you are missing the plant token or have entered an incorrect one please try again.",
|
||||
};
|
||||
}
|
||||
|
||||
if (serverInfo[0].isUpgrading) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`Looks like ${serverInfo[0].plantToken} is upgrading already you cant do this again.`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: `Looks like ${serverInfo[0].plantToken} is upgrading already you cant do this again.`,
|
||||
};
|
||||
}
|
||||
|
||||
const scriptPath = `${process.env.DEVFOLDER}\\server\\scripts\\update.ps1 `;
|
||||
const args = [
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-File",
|
||||
scriptPath,
|
||||
"-username",
|
||||
process.env.ADMUSER, // needs moved to somewhere else.
|
||||
"-admpass",
|
||||
process.env.ADMPASSWORD, // needs moved to somewhere else.
|
||||
"-devFolder",
|
||||
process.env.DEVFOLDER,
|
||||
"-server",
|
||||
serverInfo[0].serverDNS,
|
||||
"-serverIP",
|
||||
serverInfo[0].idAddress,
|
||||
"-token",
|
||||
serverInfo[0].plantToken,
|
||||
"-build",
|
||||
`${process.env.DEVFOLDER}\\builds`,
|
||||
"-location",
|
||||
serverInfo[0].serverLoc,
|
||||
"-obslst",
|
||||
serverInfo[0].oldVersion,
|
||||
"-obsBuild",
|
||||
app.admConfig.oldBuild,
|
||||
,
|
||||
];
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const process = spawn("powershell", args);
|
||||
// change the server to upgradeing
|
||||
await db
|
||||
.update(serverData)
|
||||
.set({isUpgrading: true})
|
||||
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
|
||||
//let stdout = "";
|
||||
//let stderr = "";
|
||||
|
||||
// Collect stdout data
|
||||
process.stdout.on("data", (data) => {
|
||||
const output = data.toString().trim();
|
||||
createLog("info", "lst", "serverUpdater", `${output}`);
|
||||
//onData(output);
|
||||
});
|
||||
|
||||
// Collect stderr data
|
||||
process.stderr.on("data", (data) => {
|
||||
const output = data.toString().trim();
|
||||
createLog("info", "lst", "serverUpdater", `${output}`);
|
||||
//onData(output);
|
||||
});
|
||||
|
||||
// Handle process close
|
||||
process.on("close", async (code) => {
|
||||
if (code === 0) {
|
||||
// if (count >= servers) {
|
||||
// //onClose(`Server completed with code: ${code}`);
|
||||
// }
|
||||
createLog("info", "lst", "serverUpdater", `${server}`);
|
||||
|
||||
//update the last build.
|
||||
try {
|
||||
await db
|
||||
.update(serverData)
|
||||
.set({lastUpdated: sql`NOW()`, isUpgrading: false})
|
||||
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`${server?.toLowerCase()}, has been updated and can now be used again.`
|
||||
);
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"serverUpdater",
|
||||
`There was an error updating the last time the server was updated: ${error}`
|
||||
);
|
||||
}
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
message: `${server?.toLowerCase()}, has been updated and can now be used again.`,
|
||||
});
|
||||
} else {
|
||||
const errorMessage = `Process exited with code ${code}`;
|
||||
|
||||
// if (count >= servers) {
|
||||
// //onClose(code);
|
||||
// }
|
||||
|
||||
reject({
|
||||
success: false,
|
||||
message: `${server?.toLowerCase()}, Has encounted an error while updating.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle errors with the process itself
|
||||
process.on("error", (error) => {
|
||||
//onError(err.message);
|
||||
createLog("error", "lst", "serverUpdater", `${error}`);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export async function processAllServers(devApp: string) {
|
||||
const servers = await db.select().from(serverData);
|
||||
|
||||
createLog("info", "lst", "serverUpdater", `Running the update on all servers`);
|
||||
let count = 1;
|
||||
for (const server of servers) {
|
||||
try {
|
||||
const updateToServer = await updateServer(devApp, server.plantToken);
|
||||
createLog("info", "lst", "serverUpdater", `${server.sName} was updated.`);
|
||||
count = count + 1;
|
||||
|
||||
//return {success: true, message: `${server.sName} was updated.`, data: updateToServer};
|
||||
} catch (error: any) {
|
||||
createLog("info", "lst", "serverUpdater", `Error updating ${server.sName}: ${error.message}`);
|
||||
//return {success: false, message: `Error updating ${server.sName}: ${error.message}`};
|
||||
}
|
||||
}
|
||||
}
|
||||
210
server/scripts/zipUpBuild.ts
Normal file
210
server/scripts/zipUpBuild.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
import AdmZip from "adm-zip";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { execSync } from "child_process";
|
||||
import { createLog } from "../services/logger/logger.js";
|
||||
import { getAppInfo } from "../globalUtils/appInfo.js";
|
||||
|
||||
// create the ignore list
|
||||
const ignoreList = [
|
||||
".git",
|
||||
"builds",
|
||||
"server",
|
||||
"node_modules",
|
||||
"apiDocsLSTV2",
|
||||
"testFiles",
|
||||
".env",
|
||||
".gitignore",
|
||||
".versionrc.json",
|
||||
"drizzle-dev.config.ts",
|
||||
"nssm.exe",
|
||||
"postgresql-17.2-3-windows-x64.exe",
|
||||
// front end ignore
|
||||
"frontend/node_modules",
|
||||
"fonrtend/.env",
|
||||
"frontend/public",
|
||||
"frontend/src",
|
||||
"frontend/.gitignore",
|
||||
"frontend/eslint.config.js",
|
||||
"frontend/index.html",
|
||||
"frontend/package.json",
|
||||
"frontend/package-lock.json",
|
||||
"frontend/README.md",
|
||||
"frontend/tsconfig.json",
|
||||
"frontend/tsconfig.app.json",
|
||||
"frontend/tsconfig.node.json",
|
||||
"frontend/vite.config.ts",
|
||||
"frontend/components.json",
|
||||
];
|
||||
|
||||
const shouldIgnore = (itemPath: any) => {
|
||||
const normalizedItemPath = itemPath.replace(/\\/g, "/");
|
||||
|
||||
return ignoreList.some((ignorePattern) => {
|
||||
const normalizedIgnorePatther = ignorePattern.replace(/\\/g, "/");
|
||||
return (
|
||||
normalizedItemPath === normalizedIgnorePatther ||
|
||||
normalizedItemPath.startsWith(`${normalizedIgnorePatther}/`)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const addToZip = (zip: any, currentPath: string, rootPath: string) => {
|
||||
const items = fs.readdirSync(currentPath);
|
||||
|
||||
items.forEach((item) => {
|
||||
const itemPath = path.join(currentPath, item);
|
||||
const relativePath = path.relative(rootPath, itemPath);
|
||||
|
||||
// Skip if the item is in the ignore list
|
||||
if (shouldIgnore(relativePath)) {
|
||||
createLog("info", "lst", "zipUpBuild", `Ignoring: ${relativePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const stat = fs.statSync(itemPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// If it's a directory, recursively add its contents
|
||||
addToZip(zip, itemPath, rootPath);
|
||||
} else {
|
||||
// If it's a file, add it to the zip with the preserved folder structure
|
||||
zip.addLocalFile(itemPath, path.dirname(relativePath));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateBuildNumber = (appLock: string) => {
|
||||
const packagePath = path.join(appLock, "package.json"); // Adjust path if necessary
|
||||
|
||||
try {
|
||||
// Read package.json
|
||||
const pkgData = fs.readFileSync(packagePath, "utf8");
|
||||
const pkgJson = JSON.parse(pkgData);
|
||||
|
||||
// Ensure admConfig exists
|
||||
if (pkgJson.admConfig && typeof pkgJson.admConfig.build === "number") {
|
||||
// Increment the build number
|
||||
pkgJson.admConfig.build += 1;
|
||||
|
||||
// Write the updated data back
|
||||
fs.writeFileSync(packagePath, JSON.stringify(pkgJson, null, 2), "utf8");
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Build number updated to: ${pkgJson.admConfig.build}`
|
||||
);
|
||||
// Auto-commit changes
|
||||
execSync("git add package.json");
|
||||
execSync(
|
||||
`git commit -m "build: bump build number to ${pkgJson.admConfig.build}"`
|
||||
);
|
||||
} else {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
"admConfig.build is missing or not a number"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Error updating build number: ${error}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const createZip = async (appLock: string) => {
|
||||
const app = await getAppInfo(appLock);
|
||||
const zip = new AdmZip();
|
||||
|
||||
//dest path for this app... hard coded for meow will be in db later
|
||||
const destPath = `${process.env.DEVFOLDER}\\builds`;
|
||||
const srcPath = `${process.env.DEVFOLDER}`;
|
||||
|
||||
addToZip(zip, srcPath, srcPath);
|
||||
|
||||
// Write the zip file to disk
|
||||
const outputZipPath = path.join(
|
||||
destPath,
|
||||
`${app.name}-${app.version}-${app.admConfig.build}.zip`
|
||||
);
|
||||
zip.writeZip(outputZipPath);
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Zip file created at ${outputZipPath}`
|
||||
);
|
||||
updateBuildNumber(appLock);
|
||||
|
||||
// only keep the last 5 builds for the type we have.
|
||||
try {
|
||||
const appFiles = fs
|
||||
.readdirSync(destPath)
|
||||
.filter((file) => file.startsWith(app.name)) // Ensure only backend files are matched
|
||||
.map((file) => ({
|
||||
name: file,
|
||||
time: fs.statSync(path.join(destPath, file)).mtime.getTime(),
|
||||
}))
|
||||
.sort((a, b) => a.time - b.time); // Sort by modification time (oldest first)
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`app Files (sorted by time):", ${JSON.stringify(appFiles)}`
|
||||
);
|
||||
|
||||
if (appFiles.length > 5) {
|
||||
appFiles.slice(0, -5).forEach((file) => {
|
||||
const filePath = path.join(destPath, file.name);
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
createLog("info", "lst", "zipUpBuild", `Deleted: ${file.name}`);
|
||||
} catch (error: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Failed to delete ${file.name}: ${error.message}`
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
createLog("info", "lst", "zipUpBuild", "No files to delete.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"zipUpBuild",
|
||||
`Error reading directory or deleting files:", ${error.message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//createZip("C:\\Users\\matthes01\\Documents\\lstv2");
|
||||
|
||||
// Only call `createZip` if the script is executed directly
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
const location = process.argv[2];
|
||||
|
||||
if (!location) {
|
||||
createLog("error", "lst", "zipUpBuild", "Error: No location provided.");
|
||||
process.exit(1);
|
||||
} else {
|
||||
createLog("info", "lst", "zipUpBuild", "Startiing the zip process.");
|
||||
}
|
||||
|
||||
createZip(location);
|
||||
} else {
|
||||
createLog("error", "lst", "zipUpBuild", "Error: No location provided.");
|
||||
}
|
||||
@@ -1,30 +1,37 @@
|
||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {authMiddleware} from "./middleware/authMiddleware.js";
|
||||
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import login from "./routes/login.js";
|
||||
import register from "./routes/register.js";
|
||||
import session from "./routes/session.js";
|
||||
import getAccess from "./routes/userRoles/getUserRoles.js";
|
||||
import setAccess from "./routes/userRoles/setUserRoles.js";
|
||||
import getAccess from "./routes/user/getUserRoles.js";
|
||||
import setAccess from "./routes/userAdmin/setUserRoles.js";
|
||||
import profile from "./routes/user/profileUpdate.js";
|
||||
import {areRolesIn} from "./utils/roleCheck.js";
|
||||
import { areRolesIn } from "./utils/roleCheck.js";
|
||||
import createUser from "./routes/userAdmin/createUser.js";
|
||||
import allUsers from "./routes/userAdmin/getUsers.js";
|
||||
import updateUser from "./routes/userAdmin/updateUser.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
// run the role check
|
||||
setTimeout(() => {
|
||||
areRolesIn();
|
||||
areRolesIn();
|
||||
}, 5000);
|
||||
|
||||
app.route("auth/login", login);
|
||||
app.route("auth/register", register);
|
||||
app.route("auth/session", session);
|
||||
const routes = [
|
||||
login,
|
||||
register,
|
||||
session,
|
||||
profile,
|
||||
getAccess,
|
||||
setAccess,
|
||||
createUser,
|
||||
allUsers,
|
||||
updateUser,
|
||||
] as const;
|
||||
|
||||
// required to login
|
||||
/* User area just needs to be logged in to enter here */
|
||||
app.route("auth/profileupdate", profile);
|
||||
// app.route("/server", modules);
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/auth", route);
|
||||
});
|
||||
|
||||
/* will need to increase to make sure the person coming here has the correct permissions */
|
||||
app.route("auth/getuseraccess", getAccess);
|
||||
app.route("auth/setuseraccess", setAccess);
|
||||
export default app;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {eq, sql} from "drizzle-orm";
|
||||
import {checkPassword} from "../utils/checkPassword.js";
|
||||
import {roleCheck} from "./userRoles/getUserAccess.js";
|
||||
import {createLog} from "../../logger/logger.js";
|
||||
import {differenceInDays} from "date-fns";
|
||||
|
||||
/**
|
||||
* Authenticate a user and return a JWT.
|
||||
@@ -38,7 +39,7 @@ export async function login(
|
||||
user_id: user[0].user_id,
|
||||
username: user[0].username,
|
||||
email: user[0].email,
|
||||
roles: roles || null,
|
||||
//roles: roles || null,
|
||||
role: user[0].role || null, // this should be removed onces full migration to v2 is completed
|
||||
prod: btoa(`${username.toLowerCase()}:${password}`),
|
||||
};
|
||||
@@ -50,7 +51,14 @@ export async function login(
|
||||
.set({lastLogin: sql`NOW()`})
|
||||
.where(eq(users.user_id, user[0].user_id))
|
||||
.returning({lastLogin: users.lastLogin});
|
||||
createLog("info", "lst", "auth", `Its been 5days since ${user[0].username} has logged in`);
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"auth",
|
||||
`Its been ${differenceInDays(lastLog[0]?.lastLogin ?? "", new Date(Date.now()))} days since ${
|
||||
user[0].username
|
||||
} has logged in`
|
||||
);
|
||||
//]);
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "auth", "There was an error updating the user last login");
|
||||
|
||||
24
server/services/auth/controllers/userAdmin/getUsers.ts
Normal file
24
server/services/auth/controllers/userAdmin/getUsers.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const getAllUsers = async () => {
|
||||
/**
|
||||
* returns all users that are in lst
|
||||
*/
|
||||
createLog("info", "apiAuthedRoute", "auth", "Get all users");
|
||||
const { data, error } = await tryCatch(db.select().from(users));
|
||||
|
||||
if (error) {
|
||||
returnRes(
|
||||
false,
|
||||
"There was an error getting users",
|
||||
new Error("No user exists.")
|
||||
);
|
||||
}
|
||||
|
||||
returnRes(true, "All users.", data);
|
||||
return { success: true, message: "All users", data };
|
||||
};
|
||||
68
server/services/auth/controllers/userAdmin/updateUserAdm.ts
Normal file
68
server/services/auth/controllers/userAdmin/updateUserAdm.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import type { User } from "../../../../types/users.js";
|
||||
import { createPassword } from "../../utils/createPassword.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const updateUserADM = async (userData: User) => {
|
||||
/**
|
||||
* The user model will need to be passed over so we can update per the request on the user.
|
||||
* password, username, email.
|
||||
*/
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"apiAuthedRoute",
|
||||
"auth",
|
||||
`${userData.user_id} is being updated.`
|
||||
);
|
||||
// get the orignal user info
|
||||
const { data: user, error: userError } = await tryCatch(
|
||||
db.select().from(users).where(eq(users.user_id, userData.user_id!))
|
||||
);
|
||||
|
||||
if (userError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the user",
|
||||
userError,
|
||||
};
|
||||
}
|
||||
if (user?.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
message:
|
||||
"The user you are looking for has either been deleted or dose not exist.",
|
||||
};
|
||||
}
|
||||
const upd_user = user as User;
|
||||
const password: string = userData.password
|
||||
? await createPassword(userData.password!)
|
||||
: upd_user.password!;
|
||||
const data = {
|
||||
username: userData.username ? userData.username : upd_user?.username,
|
||||
password: password,
|
||||
email: userData.email ? userData.email : upd_user.email,
|
||||
};
|
||||
|
||||
// term ? ilike(posts.title, term) : undefined
|
||||
const { data: updData, error: updError } = await tryCatch(
|
||||
db.update(users).set(data).where(eq(users.user_id, userData.user_id!))
|
||||
);
|
||||
|
||||
if (updError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the user",
|
||||
updError,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${userData.username} has been updated.`,
|
||||
updData,
|
||||
};
|
||||
};
|
||||
85
server/services/auth/middleware/roleCheck.ts
Normal file
85
server/services/auth/middleware/roleCheck.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { createMiddleware } from "hono/factory";
|
||||
|
||||
import type { CustomJwtPayload } from "../../../types/jwtToken.js";
|
||||
import { verify } from "hono/jwt";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { modules } from "../../../../database/schema/modules.js";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { userRoles } from "../../../../database/schema/userRoles.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
|
||||
const hasCorrectRole = (requiredRole: string[], module: string) =>
|
||||
createMiddleware(async (c, next) => {
|
||||
/**
|
||||
* We want to check to make sure you have the correct role to be here
|
||||
*/
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return c.json({ error: "Unauthorized" }, 401);
|
||||
}
|
||||
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
// deal with token data
|
||||
const { data: tokenData, error: tokenError } = await tryCatch(
|
||||
verify(token, process.env.JWT_SECRET!)
|
||||
);
|
||||
|
||||
if (tokenError) {
|
||||
return c.json({ error: "Invalid token" }, 401);
|
||||
}
|
||||
|
||||
const customToken = tokenData as CustomJwtPayload;
|
||||
|
||||
// Get the module
|
||||
const { data: mod, error: modError } = await tryCatch(
|
||||
db.select().from(modules).where(eq(modules.name, module))
|
||||
);
|
||||
if (modError) {
|
||||
console.log(modError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mod.length === 0) {
|
||||
return c.json({ error: "You have entered an invalid module name" }, 403);
|
||||
}
|
||||
|
||||
// check if the user has the role needed to get into this module
|
||||
const { data: userRole, error: userRoleError } = await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(userRoles)
|
||||
.where(
|
||||
and(
|
||||
eq(userRoles.module_id, mod[0].module_id),
|
||||
eq(userRoles.user_id, customToken.user?.user_id!)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (userRoleError) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userRole) {
|
||||
return c.json(
|
||||
{
|
||||
error:
|
||||
"The module you are trying to access is not active or is invalid.",
|
||||
},
|
||||
403
|
||||
);
|
||||
}
|
||||
|
||||
if (!requiredRole.includes(userRole[0]?.role)) {
|
||||
return c.json(
|
||||
{ error: "You do not have access to this part of the app." },
|
||||
403
|
||||
);
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
export default hasCorrectRole;
|
||||
@@ -1,90 +1,97 @@
|
||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {login} from "../controllers/login.js";
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { login } from "../controllers/login.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z
|
||||
.object({
|
||||
username: z.string().optional().openapi({example: "smith002"}),
|
||||
.object({
|
||||
username: z.string().optional().openapi({ example: "smith002" }),
|
||||
//email: z.string().optional().openapi({example: "s.smith@example.com"}),
|
||||
password: z.string().openapi({example: "password123"}),
|
||||
})
|
||||
.openapi("User");
|
||||
password: z.string().openapi({ example: "password123" }),
|
||||
})
|
||||
.openapi("User");
|
||||
|
||||
const route = createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Login as user",
|
||||
description: "Login as a user to get a JWT token",
|
||||
method: "post",
|
||||
path: "/",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {schema: UserSchema},
|
||||
},
|
||||
},
|
||||
tags: ["Auth"],
|
||||
summary: "Login as user",
|
||||
description: "Login as a user to get a JWT token",
|
||||
method: "post",
|
||||
path: "/login",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({example: true}),
|
||||
message: z.string().openapi({example: "Logged in"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: true }),
|
||||
message: z.string().openapi({ example: "Logged in" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({example: false}),
|
||||
message: z.string().openapi({example: "Username and password required"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Bad request",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({example: false}),
|
||||
message: z.string().openapi({example: "Username and password required"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Bad request",
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: false }),
|
||||
message: z
|
||||
.string()
|
||||
.openapi({ example: "Username and password required" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Bad request",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: false }),
|
||||
message: z
|
||||
.string()
|
||||
.openapi({ example: "Username and password required" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Bad request",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.openapi(route, async (c) => {
|
||||
const {username, password, email} = await c.req.json();
|
||||
const { username, password, email } = await c.req.json();
|
||||
|
||||
if (!username || !password) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "Username and password are required",
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
if (!username || !password) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "Username and password are required",
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const {token, user} = await login(username.toLowerCase(), password);
|
||||
try {
|
||||
const { token, user } = await login(username.toLowerCase(), password);
|
||||
|
||||
// Set the JWT as an HTTP-only cookie
|
||||
//c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`);
|
||||
// Set the JWT as an HTTP-only cookie
|
||||
//c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`);
|
||||
|
||||
return c.json({success: true, message: "Login successful", user, token}, 200);
|
||||
} catch (err) {
|
||||
return c.json({success: false, message: "Incorrect Credentials"}, 401);
|
||||
}
|
||||
return c.json(
|
||||
{ success: true, message: "Login successful", user, token },
|
||||
200
|
||||
);
|
||||
} catch (err) {
|
||||
return c.json({ success: false, message: "Incorrect Credentials" }, 401);
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
||||
@@ -1,97 +1,110 @@
|
||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {apiHit} from "../../../globalUtils/apiHits.js";
|
||||
import {registerUser} from "../controllers/register.js";
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { registerUser } from "../controllers/register.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z.object({
|
||||
username: z
|
||||
username: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_]{3,30}$/)
|
||||
.openapi({example: "smith034"}),
|
||||
email: z.string().email().openapi({example: "smith@example.com"}),
|
||||
password: z
|
||||
.openapi({ example: "smith034" }),
|
||||
email: z.string().email().openapi({ example: "smith@example.com" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(6, {message: "Passwords must be longer than 3 characters"})
|
||||
.regex(/[A-Z]/, {message: "Password must contain at least one uppercase letter"})
|
||||
.regex(/[\W_]/, {message: "Password must contain at least one special character"})
|
||||
.openapi({example: "Password1!"}),
|
||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||
.regex(/[A-Z]/, {
|
||||
message: "Password must contain at least one uppercase letter",
|
||||
})
|
||||
.regex(/[\W_]/, {
|
||||
message: "Password must contain at least one special character",
|
||||
})
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
|
||||
type User = z.infer<typeof UserSchema>;
|
||||
|
||||
const responseSchema = z.object({
|
||||
success: z.boolean().optional().openapi({example: true}),
|
||||
message: z.string().optional().openapi({example: "User Created"}),
|
||||
success: z.boolean().optional().openapi({ example: true }),
|
||||
message: z.string().optional().openapi({ example: "User Created" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Register a new user",
|
||||
method: "post",
|
||||
path: "/",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {schema: UserSchema},
|
||||
},
|
||||
},
|
||||
createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Register a new user",
|
||||
method: "post",
|
||||
path: "/register",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserSchema },
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {"application/json": {schema: responseSchema}},
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({example: false}),
|
||||
message: z.string().openapi({example: "Invalid credentials passed"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: { "application/json": { schema: responseSchema } },
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({ example: false }),
|
||||
message: z
|
||||
.string()
|
||||
.openapi({ example: "Invalid credentials passed" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
apiHit(c, {endpoint: "api/auth/register"});
|
||||
let {username, email, password} = await c.req.json();
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
apiHit(c, { endpoint: "api/auth/register" });
|
||||
let { username, email, password } = await c.req.json();
|
||||
|
||||
if (!username || !email || !password) {
|
||||
return c.json({success: false, message: "Credentials missing"}, 400);
|
||||
}
|
||||
|
||||
// some usernames that should be ignored
|
||||
const badActors = ["admin", "root"];
|
||||
if (badActors.includes(username)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} is not a valid name to be registerd please try again`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const register = await registerUser(username, password, email);
|
||||
|
||||
return c.json({success: register.success, message: register.message, user: register?.user}, 200);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
if (!username || !email || !password) {
|
||||
return c.json({ success: false, message: "Credentials missing" }, 400);
|
||||
}
|
||||
|
||||
// some usernames that should be ignored
|
||||
const badActors = ["admin", "root"];
|
||||
if (badActors.includes(username)) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} is not a valid name to be registerd please try again`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const register = await registerUser(username, password, email);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: register.success,
|
||||
message: register.message,
|
||||
user: register?.user,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
|
||||
@@ -1,97 +1,110 @@
|
||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {verify} from "hono/jwt";
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { verify } from "hono/jwt";
|
||||
|
||||
import {authMiddleware} from "../middleware/authMiddleware.js";
|
||||
import { authMiddleware } from "../middleware/authMiddleware.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
const session = new OpenAPIHono();
|
||||
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
|
||||
const secret: string = process.env.JWT_SECRET!;
|
||||
|
||||
const {sign} = jwt;
|
||||
const { sign } = jwt;
|
||||
const UserSchema = z.object({
|
||||
username: z
|
||||
username: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9_]{3,30}$/)
|
||||
.openapi({example: "smith034"}),
|
||||
email: z.string().email().openapi({example: "smith@example.com"}),
|
||||
password: z
|
||||
.openapi({ example: "smith034" }),
|
||||
email: z.string().email().openapi({ example: "smith@example.com" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(6, {message: "Passwords must be longer than 3 characters"})
|
||||
.regex(/[A-Z]/, {message: "Password must contain at least one uppercase letter"})
|
||||
.regex(/[\W_]/, {message: "Password must contain at least one special character"})
|
||||
.openapi({example: "Password1!"}),
|
||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||
.regex(/[A-Z]/, {
|
||||
message: "Password must contain at least one uppercase letter",
|
||||
})
|
||||
.regex(/[\W_]/, {
|
||||
message: "Password must contain at least one special character",
|
||||
})
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
|
||||
session.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Checks a user session based on there token",
|
||||
description: "Can post there via Authentiaction header or cookies",
|
||||
method: "get",
|
||||
path: "/",
|
||||
middleware: authMiddleware,
|
||||
// request: {
|
||||
// body: {
|
||||
// content: {
|
||||
// "application/json": {schema: UserSchema},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
data: z.object({
|
||||
token: z.string().openapi({example: "sdkjhgsldkvhdakl;jvhs;adkjfhvds.kvnsad;ovhads"}),
|
||||
// user: z.object({
|
||||
// user_id: z.string().openapi({example: "04316c86-f086-4cc6-b3d4-cca164a26f3f"}),
|
||||
// username: z.string().openapi({example: "smith"}),
|
||||
// email: z.string().openapi({example: "smith@example.com"}).optional(),
|
||||
// }),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Login successful",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string().openapi({example: "Unathenticated"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Error of why you were not logged in.",
|
||||
},
|
||||
createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Checks a user session based on there token",
|
||||
description: "Can post there via Authentiaction header or cookies",
|
||||
method: "get",
|
||||
path: "/session",
|
||||
middleware: authMiddleware,
|
||||
// request: {
|
||||
// body: {
|
||||
// content: {
|
||||
// "application/json": {schema: UserSchema},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
data: z.object({
|
||||
token: z
|
||||
.string()
|
||||
.openapi({
|
||||
example: "sdkjhgsldkvhdakl;jvhs;adkjfhvds.kvnsad;ovhads",
|
||||
}),
|
||||
// user: z.object({
|
||||
// user_id: z.string().openapi({example: "04316c86-f086-4cc6-b3d4-cca164a26f3f"}),
|
||||
// username: z.string().openapi({example: "smith"}),
|
||||
// email: z.string().openapi({example: "smith@example.com"}).optional(),
|
||||
// }),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const authHeader = c.req.header("Authorization");
|
||||
description: "Login successful",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string().openapi({ example: "Unathenticated" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Error of why you were not logged in.",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (authHeader?.includes("Basic")) {
|
||||
return c.json({message: "You are a Basic user! Please login to get a token"}, 401);
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return c.json({message: "Unauthorized"}, 401);
|
||||
}
|
||||
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
|
||||
// If it's valid, return a new token
|
||||
const newToken = sign({user: payload.user}, secret, {expiresIn: expiresIn * 60});
|
||||
|
||||
return c.json({data: {token: newToken, user: payload.user}}, 200);
|
||||
} catch (error) {
|
||||
return c.json({message: "Unauthorized"}, 401);
|
||||
}
|
||||
if (authHeader?.includes("Basic")) {
|
||||
return c.json(
|
||||
{ message: "You are a Basic user! Please login to get a token" },
|
||||
401
|
||||
);
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return c.json({ message: "Unauthorized" }, 401);
|
||||
}
|
||||
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
|
||||
// If it's valid, return a new token
|
||||
const newToken = sign({ user: payload.user }, secret, {
|
||||
expiresIn: expiresIn * 60,
|
||||
});
|
||||
|
||||
return c.json({ data: { token: newToken, user: payload.user } }, 200);
|
||||
} catch (error) {
|
||||
return c.json({ message: "Unauthorized" }, 401);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// const token = authHeader?.split("Bearer ")[1] || "";
|
||||
|
||||
59
server/services/auth/routes/user/getUserRoles.ts
Normal file
59
server/services/auth/routes/user/getUserRoles.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import jwt from "jsonwebtoken";
|
||||
import type { CustomJwtPayload } from "../../../../types/jwtToken.js";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import { roleCheck } from "../../controllers/userRoles/getUserAccess.js";
|
||||
|
||||
const { verify } = jwt;
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const responseSchema = z.object({
|
||||
message: z.string().optional().openapi({ example: "User Created" }),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["auth:user"],
|
||||
summary: "returns the users access",
|
||||
method: "get",
|
||||
path: "/getuseraccess",
|
||||
middleware: [authMiddleware],
|
||||
responses: {
|
||||
200: {
|
||||
content: { "application/json": { schema: responseSchema } },
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// apit hit
|
||||
//apiHit(c, { endpoint: "api/auth/getUserRoles" });
|
||||
const authHeader = c.req.header("Authorization");
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
try {
|
||||
const secret = process.env.JWT_SECRET!;
|
||||
if (!secret) {
|
||||
throw new Error("JWT_SECRET is not defined in environment variables");
|
||||
}
|
||||
|
||||
const payload = verify(token, secret) as CustomJwtPayload;
|
||||
|
||||
const canAccess = await roleCheck(payload.user?.user_id);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
sucess: true,
|
||||
message: `User ${payload.user?.username} can access`,
|
||||
data: canAccess,
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
return c.json({ message: "UserRoles coming over" });
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
@@ -1,95 +1,120 @@
|
||||
import {createRoute, OpenAPIHono, z} from "@hono/zod-openapi";
|
||||
import {authMiddleware} from "../../middleware/authMiddleware.js";
|
||||
import {updateProfile} from "../../controllers/users/updateProfile.js";
|
||||
import {verify} from "hono/jwt";
|
||||
import {createLog} from "../../../logger/logger.js";
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { authMiddleware } from "../../middleware/authMiddleware.js";
|
||||
import { updateProfile } from "../../controllers/users/updateProfile.js";
|
||||
import { verify } from "hono/jwt";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z.object({
|
||||
password: z
|
||||
password: z
|
||||
.string()
|
||||
.min(6, {message: "Passwords must be longer than 3 characters"})
|
||||
.regex(/[A-Z]/, {message: "Password must contain at least one uppercase letter"})
|
||||
.regex(/[\W_]/, {message: "Password must contain at least one special character"})
|
||||
.openapi({example: "Password1!"}),
|
||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||
.regex(/[A-Z]/, {
|
||||
message: "Password must contain at least one uppercase letter",
|
||||
})
|
||||
.regex(/[\W_]/, {
|
||||
message: "Password must contain at least one special character",
|
||||
})
|
||||
.openapi({ example: "Password1!" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["User"],
|
||||
summary: "Updates a users Profile",
|
||||
description: "Currently you can only update your password over the API",
|
||||
method: "post",
|
||||
path: "/",
|
||||
middleware: authMiddleware,
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {schema: UserSchema},
|
||||
},
|
||||
},
|
||||
createRoute({
|
||||
tags: ["auth:user"],
|
||||
summary: "Updates a users Profile",
|
||||
description: "Currently you can only update your password over the API",
|
||||
method: "post",
|
||||
path: "/profile",
|
||||
middleware: authMiddleware,
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: UserSchema },
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string().optional().openapi({example: "User Profile has been updated"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Sucess return",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Unauthenticated"})}),
|
||||
},
|
||||
},
|
||||
description: "Unauthorized",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z
|
||||
.string()
|
||||
.optional()
|
||||
.openapi({ example: "User Profile has been updated" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
const authHeader = c.req.header("Authorization");
|
||||
description: "Sucess return",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z
|
||||
.string()
|
||||
.optional()
|
||||
.openapi({ example: "Unauthenticated" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Unauthorized",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z
|
||||
.string()
|
||||
.optional()
|
||||
.openapi({ example: "Internal Server error" }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (authHeader?.includes("Basic")) {
|
||||
return c.json({message: "You are a Basic user! Please login to get a token"}, 401);
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return c.json({message: "Unauthorized"}, 401);
|
||||
}
|
||||
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
let user;
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
user = payload.user;
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "auth", "Failed session check, user must be logged out");
|
||||
return c.json({message: "Unauthorized"}, 401);
|
||||
}
|
||||
|
||||
// now pass all the data over to update the user info
|
||||
try {
|
||||
const data = await c?.req.json();
|
||||
await updateProfile(user, data, token);
|
||||
return c.json({message: "Your profile has been updated"});
|
||||
} catch (error) {
|
||||
return c.json({message: "There was an error", error});
|
||||
}
|
||||
if (authHeader?.includes("Basic")) {
|
||||
return c.json(
|
||||
{ message: "You are a Basic user! Please login to get a token" },
|
||||
401
|
||||
);
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return c.json({ message: "Unauthorized" }, 401);
|
||||
}
|
||||
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
let user;
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
user = payload.user;
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"lst",
|
||||
"auth",
|
||||
"Failed session check, user must be logged out"
|
||||
);
|
||||
return c.json({ message: "Unauthorized" }, 401);
|
||||
}
|
||||
|
||||
// now pass all the data over to update the user info
|
||||
try {
|
||||
const data = await c?.req.json();
|
||||
await updateProfile(user, data, token);
|
||||
return c.json({ message: "Your profile has been updated" });
|
||||
} catch (error) {
|
||||
return c.json({ message: "There was an error", error });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user