Compare commits

...

30 Commits

Author SHA1 Message Date
c35726bf5c fix(lst): missing , in versionRc 2025-03-23 11:11:06 -05:00
e82208fc5e build(build): removed from showing in the change log 2025-03-23 11:10:00 -05:00
9bdca3317c chore: bump build number to 50 2025-03-23 11:06:13 -05:00
61abd44328 chore(release): 2.9.0 2025-03-23 11:05:26 -05:00
acb9876d9b chore: bump build number to 49 2025-03-23 11:02:22 -05:00
10d88f53eb fix(frontend): removed unwanted import 2025-03-23 11:01:21 -05:00
bba0aa2ee4 refactor(frontend): added date-fns into the frontend 2025-03-23 11:00:38 -05:00
b7773ec02a fix(sqlserver): if we already have a connection just return we dont want to try a second time 2025-03-23 11:00:15 -05:00
b9dd6e3ae2 fix(auth): fixed the getaccess to be getuseraccess as it was orignally 2025-03-23 10:59:48 -05:00
f940bcdc9d fix(serverlist): corrected the time by removing teh Z at the end of the time 2025-03-23 10:59:23 -05:00
2ae3c8ba59 fix(loginform): removed the console log that was left by accident 2025-03-23 10:58:49 -05:00
97b9c4db4a chore: bump build number to 48 2025-03-23 10:16:28 -05:00
d8eddafcae feat(auth): add, update were added for adm account in backend only 2025-03-23 10:07:28 -05:00
58b58424ab test(returnres): tryed to make a standard return res but will come back to this later 2025-03-23 10:06:34 -05:00
72d52d9256 feat(trycatch): added in theo's try catch to reduce the code and love it 2025-03-23 10:05:51 -05:00
5dfece09b7 refactor(login): removed all the data from teh login dropdown as it could cause issues 2025-03-23 10:05:27 -05:00
97eb73c6d1 test(streaming logs): will come back to this later this is killing me inside 2025-03-23 10:04:58 -05:00
bb6d523abb refactor(server): removed the websocket wrapper going wiht normal ws 2025-03-22 10:06:14 -05:00
09d3a7041a docs(install): added more env params and an install install stuff 2025-03-22 10:05:47 -05:00
132e8d0146 feat(lst): added prettier config so we have the same formatting across all computers 2025-03-22 10:05:10 -05:00
d0a0d08902 feat(scripts): made moving scripts more proper 2025-03-22 10:04:48 -05:00
208cd615af refactor(auth): added in correct bycrptjs 2025-03-22 10:04:14 -05:00
e82ef76316 test(streaming): more testing on streaming the lofs 2025-03-22 08:21:11 -05:00
4db4eea2d1 test(streaming): more streaming test 2025-03-21 22:08:13 -05:00
edcfff6cc6 fix(scaler): fix due to update 2025-03-21 22:07:49 -05:00
61860c759e build(updater): dynamic ports mainly for iowa2 2025-03-21 22:07:32 -05:00
08c9b3404f test(streaming logs): more test but failed again 2025-03-21 22:06:53 -05:00
93ed2e9ee8 fix(calendar): this component had a bug and needed a lib update 2025-03-21 22:06:25 -05:00
fb9ee15bda chore(pkg updates): updated all pkgs 2025-03-21 21:48:04 -05:00
76bc0db3dd test(server): start/stop/restart buttons added. comment #14 2025-03-21 20:09:39 -05:00
52 changed files with 5202 additions and 6565 deletions

View File

@@ -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
View 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
}
}

View File

@@ -1,5 +1,130 @@
# 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)

View File

@@ -0,0 +1,2 @@
DROP TABLE "apiHits" CASCADE;--> statement-breakpoint
DROP TABLE "eom" CASCADE;

File diff suppressed because it is too large Load Diff

View File

@@ -176,6 +176,13 @@
"when": 1742408812383,
"tag": "0024_curved_venom",
"breakpoints": true
},
{
"idx": 25,
"version": "7",
"when": 1742655504936,
"tag": "0025_amusing_sugar_man",
"breakpoints": true
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@
"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",
@@ -25,46 +25,47 @@
"@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",
"npm-check-updates": "^17.1.15",
"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"
}
}

View 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>
);
}

View File

@@ -1,98 +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 { 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 { 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;
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 { user, token } = useSessionStore();
const { modules } = useModuleStore();
const router = useRouter();
const {data, isError, error, isLoading} = useQuery(getServers(token ?? ""));
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) || [];
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 (!adminModule[0]?.roles?.includes(userLevel[0]?.role)) {
router.navigate({ to: "/" });
}
if (isError) {
return <div>{JSON.stringify(error)}</div>;
}
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) => (
<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">
{format(server.lastUpdated, "MM/dd/yyyy hh:mm")}
</TableCell>
<TableCell className="font-medium">
{adminUrlCheck() && <UpdateServer server={server} token={token as string} />}
</TableCell>
</TableRow>
))}
</TableBody>
)}
</Table>
</LstCard>
);
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>
);
}

View 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>
);
}

View 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>
);
}

View File

@@ -5,6 +5,7 @@ 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 ?? ""));
@@ -33,9 +34,18 @@ export default function UpdateServer({server, token}: {server: Servers; token: s
};
return (
<div>
<Button variant={"outline"} size={"icon"} onClick={upgrade} disabled={server.isUpgrading}>
<CircleFadingArrowUp />
</Button>
<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>
);
}

View File

@@ -1,153 +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);
if (data.success) {
const prod = btoa(`${value.username.toLowerCase()}:${value.password}`);
const prodUser = {...data.user, prod: prod};
//console.log(data);
} catch (err) {
toast.error("Invalid credentials");
}
};
setSession(prodUser, data.token);
toast.success(`You are logged in as ${data.user.username}`);
router.navigate({to: "/"});
}
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>
if (!data.success) {
toast.error(`${data.message}`);
}
console.log(data);
} 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>
<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;

View File

@@ -1,46 +1,71 @@
//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";
/**
* we will do a very sloppy way for this just keep fetching the logs we spent to much time on this :()
*/
export default function CycleCountLog() {
//const [logs, setLogs] = useState([]);
//const [streaming, setStreaming] = useState(false); // Track if streaming is active
// //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";
// useEffect(() => {
// // Start streaming when the button is clicked
// let es;
// export default function CycleCountLog() {
// //const [logs, setLogs] = useState([]);
// //const [streaming, setStreaming] = useState(false); // Track if streaming is active
// es = new EventSource("http://localhost:4000/api/logger/logs/stream?service=ocme-count&level=info");
// es.onopen = () => console.log(">>> Connection opened!");
// es.onerror = (e) => console.log("ERROR!", e);
// es.onmessage = (e) => {
// console.log(">>>", JSON.stringify(e));
// };
// useEffect(() => {
// // Start streaming when the button is clicked
// let es;
// return () => es.close();
// }, []); // Effect runs when `streaming` state changes
// const url = `http://localhost:4000/api/logger/logs/stream?service=ocme-count&level=info`;
// 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>
);
}
// 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>
// );
// }

View File

@@ -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: {

View File

@@ -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}

View File

@@ -1,91 +1,94 @@
import {createRootRoute, Link, Outlet} from "@tanstack/react-router";
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" /> */}
</>
);
},
});

View File

@@ -18,8 +18,16 @@ export default defineConfig({
},
server: {
proxy: {
"/api": {target: `http://localhost:${Number(process.env.VITE_SERVER_PORT || 4400)}`, changeOrigin: true},
"/ocme": {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,
},
},
},
});

6243
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "lstv2",
"version": "2.8.0",
"version": "2.9.0",
"type": "module",
"scripts": {
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
@@ -9,8 +9,10 @@
"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 build && tsc --build && xcopy server\\scripts dist\\server\\scripts /E /I /Y && xcopy server\\services\\server\\utils\\serverData.json dist\\server\\services\\server\\utils /E /I /Y ",
"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",
@@ -22,50 +24,7 @@
"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": "npm-check-updates"
},
"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",
"adm-zip": "^0.5.16",
"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",
"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.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/adm-zip": "^0.5.7",
"@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"
"checkupdates": "npx npm-check-updates"
},
"config": {
"commitizen": {
@@ -73,7 +32,50 @@
}
},
"admConfig": {
"build": 47,
"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"
}
}

View 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);
// }

View 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 };
}
}

View File

@@ -1,12 +1,12 @@
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
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 { 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";
@@ -19,9 +19,9 @@ import rfid from "./services/rfid/rfidService.js";
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, eq} from "drizzle-orm";
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";
@@ -29,63 +29,66 @@ 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 app = new OpenAPIHono({strict: false});
const app = new OpenAPIHono({ strict: false });
// middle ware
app.use("*", logger());
app.use(
"*",
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,
})
"*",
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();
//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();
}
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();
}
// 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,
printers,
loggerService,
ocpService,
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);
});
app.route("/ocme/", ocme);
@@ -118,46 +121,56 @@ app.route("/ocme/", ocme);
//---------------------------------------------------\\
// the catch all api route
app.all("/api/*", (c) => c.json({error: "API route not found"}, 404));
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;
const port =
process.env.NODE_ENV === "development"
? process.env.VITE_SERVER_PORT
: process.env.PROD_PORT;
serve(
{
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}`);
}
{
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;

View 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);
});

View File

@@ -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,7 +68,7 @@ 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"
@@ -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,11 +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.
- 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.

View File

@@ -254,16 +254,29 @@ API_KEY= "E3ECD3619A943B98C6F33E3322362"
Write-Host "Creating the env file in the front end"
$envContentTemplateb = @"
DATABASE_URL="file:E:\LST\db\lstBackendDB.db"
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
$envContentTemplateb | Out-File -FilePath $envFilePathb -Encoding UTF8 -Force
$envContentb | Out-File -FilePath $envFilePathb -Encoding UTF8 -Force
# Optional: Verify the file was created
if (Test-Path $envFilePathb) {
@@ -287,12 +300,19 @@ NODE_ENV = production
# server apiKey
API_KEY = E3ECD3619A943B98C6F33E3322362
# Prisma DB link
DATABASE_URL="file:E:\LST\db\lstBackendDB.db"
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\lstBackendDB.db"
DATEBASE_LOC="E:\LST\db\{dbLink}.db"
DATABASE_BACKUP_LOC="E:\LST\backups"
# Server port
SERVER_PORT = 4400
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"
@@ -310,13 +330,39 @@ COOKIE_EXPIRES_TIME = 60
RESET_TOKEN = 330
"@
try {
# Define the path where the .env file should be created
$envFilePathd = $obslst + "\.env"
# Write the content to the .env file
$envContentTemplated | Out-File -FilePath $envFilePathd -Encoding UTF8 -Force
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
# 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) {

View File

@@ -1,153 +1,193 @@
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";
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",
".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, "/");
const normalizedItemPath = itemPath.replace(/\\/g, "/");
return ignoreList.some((ignorePattern) => {
const normalizedIgnorePatther = ignorePattern.replace(/\\/g, "/");
return (
normalizedItemPath === normalizedIgnorePatther ||
normalizedItemPath.startsWith(`${normalizedIgnorePatther}/`)
);
});
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);
const items = fs.readdirSync(currentPath);
items.forEach((item) => {
const itemPath = path.join(currentPath, item);
const relativePath = path.relative(rootPath, itemPath);
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;
}
// Skip if the item is in the ignore list
if (shouldIgnore(relativePath)) {
createLog("info", "lst", "zipUpBuild", `Ignoring: ${relativePath}`);
return;
}
const stat = fs.statSync(itemPath);
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));
}
});
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
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);
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;
// 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");
// 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 "chore: 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}`);
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();
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}`;
//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);
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);
// 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);
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)
// 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)}`);
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.");
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}`
);
}
} catch (error: any) {
createLog("error", "lst", "zipUpBuild", `Error reading directory or deleting files:", ${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");
@@ -155,16 +195,16 @@ export const createZip = async (appLock: string) => {
// Only call `createZip` if the script is executed directly
if (process.argv.length > 2) {
const location = process.argv[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 {
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.");
}

View File

@@ -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;

View 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 };
};

View 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,
};
};

View 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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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] || "";

View 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;

View File

@@ -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;

View File

@@ -0,0 +1,115 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { registerUser } from "../../controllers/register.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
const app = new OpenAPIHono();
const UserSchema = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.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!" }),
});
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" }),
});
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "Creates user",
method: "post",
path: "/",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
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",
},
},
}),
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
);
}
}
);
export default app;

View File

@@ -0,0 +1,35 @@
import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { getAllUsers } from "../../controllers/userAdmin/getUsers.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
const app = new OpenAPIHono();
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "Gets Users",
method: "get",
path: "/allusers",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
responses: responses(),
}),
async (c) => {
// apit hit
//apiHit(c, {endpoint: "api/auth/register"});
const allUsers: any = await getAllUsers();
return c.json({
success: allUsers?.success,
message: allUsers?.message,
data: allUsers?.data,
});
}
);
export default app;

View File

@@ -0,0 +1,71 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { setUserAccess } from "../../controllers/userRoles/setUserRoles.js";
import { apiHit } from "../../../../globalUtils/apiHits.js";
import { apiReturn } from "../../../../globalUtils/apiReturn.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
const app = new OpenAPIHono();
const responseSchema = z.object({
success: z.boolean().openapi({ example: true }),
message: z.string().optional().openapi({ example: "user access" }),
data: z.array(z.object({})).optional().openapi({ example: [] }),
});
const UserAccess = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({ example: "smith034" }),
module: z.string().openapi({ example: "production" }),
role: z.string().openapi({ example: "viewer" }),
override: z.string().optional().openapi({ example: "secretString" }),
});
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "Sets Users access",
method: "post",
path: "/setuseraccess",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
description: "When logged in you will be able to grant new permissions",
request: {
body: {
content: {
"application/json": { schema: UserAccess },
},
},
},
responses: responses(),
}),
async (c) => {
//apiHit(c, { endpoint: "api/auth/setUserRoles" });
const { username, module, role, override } = await c.req.json();
try {
const access = await setUserAccess(username, module, role, override);
//return apiReturn(c, true, access?.message, access?.data, 200);
return c.json(
{ success: access.success, message: access.message, data: access.data },
200
);
} catch (error) {
console.log(error);
//return apiReturn(c, false, "Error in setting the user access", error, 400);
return c.json(
{
success: false,
message: "Error in setting the user access",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -0,0 +1,91 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { setUserAccess } from "../../controllers/userRoles/setUserRoles.js";
import { apiHit } from "../../../../globalUtils/apiHits.js";
import { apiReturn } from "../../../../globalUtils/apiReturn.js";
import { authMiddleware } from "../../middleware/authMiddleware.js";
import hasCorrectRole from "../../middleware/roleCheck.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { updateUserADM } from "../../controllers/userAdmin/updateUserAdm.js";
const app = new OpenAPIHono();
const responseSchema = z.object({
success: z.boolean().openapi({ example: true }),
message: z.string().optional().openapi({ example: "user access" }),
data: z.array(z.object({})).optional().openapi({ example: [] }),
});
const UserAccess = z.object({
user_id: z.string().openapi({ example: "users UUID" }),
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.optional()
.openapi({ example: "smith034" }),
email: z
.string()
.email()
.optional()
.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",
})
.optional()
.openapi({ example: "Password1!" }),
});
app.openapi(
createRoute({
tags: ["Auth:admin"],
summary: "updates a specific user",
method: "post",
path: "/updateuser",
middleware: [
authMiddleware,
hasCorrectRole(["admin", "systemAdmin"], "admin"),
],
//description: "When logged in you will be able to grant new permissions",
request: {
body: {
content: {
"application/json": { schema: UserAccess },
},
},
},
responses: responses(),
}),
async (c) => {
//apiHit(c, { endpoint: "api/auth/setUserRoles" });
const userData = await c.req.json();
try {
const userUPD: any = await updateUserADM(userData);
//return apiReturn(c, true, access?.message, access?.data, 200);
return c.json(
{
success: userUPD.success,
message: userUPD.message,
data: userUPD.data,
},
200
);
} catch (error) {
console.log(error);
//return apiReturn(c, false, "Error in setting the user access", error, 400);
return c.json(
{
success: false,
message: "Error in setting the user access",
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -1,53 +0,0 @@
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
import {apiHit} from "../../../../globalUtils/apiHits.js";
import jwt from "jsonwebtoken";
import {roleCheck} from "../../controllers/userRoles/getUserAccess.js";
import type {CustomJwtPayload} from "../../../../types/jwtToken.js";
import {authMiddleware} from "../../middleware/authMiddleware.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"],
summary: "Returns the useraccess table",
method: "get",
path: "/",
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;

View File

@@ -1,65 +0,0 @@
import {createRoute, OpenAPIHono, z} from "@hono/zod-openapi";
import {setUserAccess} from "../../controllers/userRoles/setUserRoles.js";
import {apiHit} from "../../../../globalUtils/apiHits.js";
import {apiReturn} from "../../../../globalUtils/apiReturn.js";
import {authMiddleware} from "../../middleware/authMiddleware.js";
const app = new OpenAPIHono();
const responseSchema = z.object({
success: z.boolean().openapi({example: true}),
message: z.string().optional().openapi({example: "user access"}),
data: z.array(z.object({})).optional().openapi({example: []}),
});
const UserAccess = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({example: "smith034"}),
module: z.string().openapi({example: "production"}),
role: z.string().openapi({example: "viewer"}),
override: z.string().optional().openapi({example: "secretString"}),
});
app.openapi(
createRoute({
tags: ["Auth"],
summary: "Sets Users access",
method: "post",
path: "/",
middleware: authMiddleware,
description: "When logged in you will be able to grant new permissions",
request: {
body: {
content: {
"application/json": {schema: UserAccess},
},
},
},
responses: {
200: {
content: {"application/json": {schema: responseSchema}},
description: "Retrieve the user",
},
400: {
content: {"application/json": {schema: responseSchema}},
description: "Failed to get user access",
},
},
}),
async (c) => {
apiHit(c, {endpoint: "api/auth/setUserRoles"});
const {username, module, role, override} = await c.req.json();
try {
const access = await setUserAccess(username, module, role, override);
//return apiReturn(c, true, access?.message, access?.data, 200);
return c.json({success: access.success, message: access.message, data: access.data}, 200);
} catch (error) {
console.log(error);
//return apiReturn(c, false, "Error in setting the user access", error, 400);
return c.json({success: false, message: "Error in setting the user access", data: error}, 400);
}
}
);
export default app;

View File

@@ -1,16 +1,18 @@
import bcrypt from "bcrypt";
import bcrypt from "bcryptjs";
export const checkPassword = async (
currentPassword: string,
dbPassword: string
) => {
let decyptPass = "";
try {
decyptPass = atob(dbPassword);
} catch (error) {
console.log(error);
}
// encypt password
const pass: string | undefined = process.env.SECRET;
export const checkPassword = async (currentPassword: string, dbPassword: string) => {
let decyptPass = "";
try {
decyptPass = atob(dbPassword);
} catch (error) {
console.log(error);
}
// encypt password
const pass: string | undefined = process.env.SECRET;
const checked = bcrypt.compareSync(pass + currentPassword, decyptPass);
const checked = bcrypt.compareSync(pass + currentPassword, decyptPass);
return checked;
return checked;
};

View File

@@ -1,17 +1,17 @@
import bcrypt from "bcrypt";
import bcrypt from "bcryptjs";
export const createPassword = async (password: string) => {
// encypt password
let pass: string | undefined = process.env.SECRET;
let salt: string | undefined = process.env.SALTING;
// encypt password
let pass: string | undefined = process.env.SECRET;
let salt: string | undefined = process.env.SALTING;
if (!pass || !salt) {
pass = "error";
} else {
pass = bcrypt.hashSync(pass + password, parseInt(salt));
if (!pass || !salt) {
pass = "error";
} else {
pass = bcrypt.hashSync(pass + password, parseInt(salt));
pass = btoa(pass);
}
pass = btoa(pass);
}
return pass;
return pass;
};

View File

@@ -55,9 +55,9 @@ app.get(
"nsurlsession",
"undici",
],
spec: {
url: "/api/ref",
},
url: "/api/ref",
baseServerURL: "https://scalar.com",
servers: [
{

View File

@@ -1,7 +1,7 @@
import type {Context} from "hono";
import {db} from "../../../../database/dbclient.js";
import {and, eq, gt} from "drizzle-orm";
import {streamSSE} from "hono/streaming";
import {streamSSE, streamText} from "hono/streaming";
import {logs} from "../../../../database/schema/logs.js";
export async function streamLogs(c: Context) {

View File

@@ -29,7 +29,23 @@ app.openapi(
}),
async (c) => {
apiHit(c, {endpoint: `api/logger/logs`});
return streamLogs(c);
c.header("Content-Type", "text/event-stream");
c.header("Cache-Control", "no-cache");
c.header("Connection", "keep-alive");
return streamSSE(c, async (stream) => {
let id = 0;
const encoder = new TextEncoder();
while (true) {
const message = `It is ${new Date().toISOString()}`;
await stream.writeSSE({
data: message,
event: "time-update",
id: String(id++),
});
encoder.encode(`data: ${JSON.stringify({type: "progress", data: id})}\n\n`);
await stream.sleep(1000);
}
});
}
);
export default app;

View File

@@ -1,7 +1,7 @@
{
"servers": [
{
"sName": "test",
"sName": "Test",
"serverDNS": "usmcd1vms036",
"plantToken": "test3",
"idAdress": "10.193.0.56",
@@ -33,7 +33,7 @@
"contactPhone": "6366970253",
"customerTiAcc": "ALPL01BETHINT",
"lstServerPort": "4000",
"active": false,
"active": true,
"serverLoc": "E:\\LST\\lstv2",
"oldVersion": "E:\\LST\\lst_backend",
"shippingHours": "[{\"early\": \"06:30\", \"late\": \"23:00\"}]",

View File

@@ -1,111 +1,198 @@
import sql from "mssql";
import {prodSqlConfig} from "./utils/prodServerConfig.js";
import {createLog} from "../logger/logger.js";
import {db} from "../../../database/dbclient.js";
import {settings} from "../../../database/schema/settings.js";
import {eq} from "drizzle-orm";
import {installed} from "../../index.js";
import {checkHostnamePort} from "../../globalUtils/pingServer.js";
import { prodSqlConfig } from "./utils/prodServerConfig.js";
import { createLog } from "../logger/logger.js";
import { db } from "../../../database/dbclient.js";
import { settings } from "../../../database/schema/settings.js";
import { eq } from "drizzle-orm";
import { installed } from "../../index.js";
import { checkHostnamePort } from "../../globalUtils/pingServer.js";
let pool: any;
let connected: boolean = false;
export const initializeProdPool = async () => {
if (!installed) {
createLog("info", "lst", "sqlProd", "The server was not installed will reconnect in 5 seconds");
setTimeout(() => {
initializeProdPool();
}, 5 * 1000);
if (!installed) {
createLog(
"info",
"lst",
"sqlProd",
"The server was not installed will reconnect in 5 seconds"
);
setTimeout(() => {
initializeProdPool();
}, 5 * 1000);
return {success: false, message: "The server is not installed."};
}
const dbServer = await db.select().from(settings).where(eq(settings.name, "dbServer"));
const serverUp = await checkHostnamePort(`${dbServer[0].value}:1433`);
return { success: false, message: "The server is not installed." };
}
const dbServer = await db
.select()
.from(settings)
.where(eq(settings.name, "dbServer"));
const serverUp = await checkHostnamePort(`${dbServer[0].value}:1433`);
if (!serverUp) {
createLog("error", "lst", "server", `The sql ${dbServer[0].value} is not reachable`);
return {success: false, message: `The sql ${dbServer[0].value} is not reachable`};
}
if (!serverUp) {
createLog(
"error",
"lst",
"server",
`The sql ${dbServer[0].value} is not reachable`
);
return {
success: false,
message: `The sql ${dbServer[0].value} is not reachable`,
};
}
// make sure the server is not set to localhost this will prevent some weird issues later but can be localhost on the dev
const serverLoc = await db.select().from(settings).where(eq(settings.name, "dbServer"));
if (serverLoc[0].value === "localhost" && process.env.NODE_ENV !== "development") {
createLog("error", "lst", "sqlProd", "The server is set to localhost, and you are not in development mode.");
return {success: false, message: "The server is set to localhost, and you are not in development mode."};
}
// make sure the server is not set to localhost this will prevent some weird issues later but can be localhost on the dev
const serverLoc = await db
.select()
.from(settings)
.where(eq(settings.name, "dbServer"));
if (
serverLoc[0].value === "localhost" &&
process.env.NODE_ENV !== "development"
) {
createLog(
"error",
"lst",
"sqlProd",
"The server is set to localhost, and you are not in development mode."
);
return {
success: false,
message:
"The server is set to localhost, and you are not in development mode.",
};
}
// if you were restarting from the endpoint you get this lovely error
if (connected) {
createLog("error", "lst", "sqlProd", "There is already a connection.");
return {success: false, message: "There is already a connection."};
}
try {
const config = await prodSqlConfig();
pool = await sql.connect(config!);
// if you were restarting from the endpoint you get this lovely error
if (connected) {
createLog("error", "lst", "sqlProd", "There is already a connection.");
return { success: false, message: "There is already a connection." };
}
try {
const config = await prodSqlConfig();
pool = await sql.connect(config!);
createLog("info", "lst", "sqlProd", `Connected to ${config?.server}, and looking at ${config?.database}`);
connected = true;
return {success: true, message: "The sql server connection has been closed"};
} catch (error) {
createLog("error", "lst", "sqlProd", `${JSON.stringify(error)}, "There was an error connecting to the pool."`);
throw new Error("There was an error closing the sql connection");
}
createLog(
"info",
"lst",
"sqlProd",
`Connected to ${config?.server}, and looking at ${config?.database}`
);
connected = true;
return {
success: true,
message: "The sql server connection has been closed",
};
} catch (error) {
createLog(
"error",
"lst",
"sqlProd",
`${JSON.stringify(error)}, "There was an error connecting to the pool."`
);
throw new Error("There was an error closing the sql connection");
}
};
export const closePool = async () => {
try {
await pool.close();
createLog("info", "lst", "sqlProd", "Connection pool closed");
connected = false;
return {success: true, message: "The sql server connection has been closed"};
} catch (error) {
createLog(
"error",
"lst",
"sqlProd",
`${JSON.stringify(error)}, "There was an error closing the sql connection"`
);
throw new Error("There was an error closing the sql connection");
}
if (!connected) {
createLog(
"error",
"lst",
"sqlProd",
"There is no connection a connection."
);
return { success: false, message: "There is already a connection." };
}
try {
await pool.close();
createLog("info", "lst", "sqlProd", "Connection pool closed");
connected = false;
return {
success: true,
message: "The sql server connection has been closed",
};
} catch (error) {
createLog(
"error",
"lst",
"sqlProd",
`${JSON.stringify(
error
)}, "There was an error closing the sql connection"`
);
throw new Error("There was an error closing the sql connection");
}
};
export async function query(queryToRun: string, name: string) {
/**
* Just an extra catch incase someone tried to run a query while we were not connected to the server or sql server
*/
const dbServer = await db.select().from(settings).where(eq(settings.name, "dbServer"));
const serverUp = await checkHostnamePort(`${dbServer[0].value}:1433`);
/**
* Just an extra catch incase someone tried to run a query while we were not connected to the server or sql server
*/
const dbServer = await db
.select()
.from(settings)
.where(eq(settings.name, "dbServer"));
const serverUp = await checkHostnamePort(`${dbServer[0].value}:1433`);
if (!serverUp) {
createLog("error", "lst", "server", `The sql ${dbServer[0].value} is not reachable`);
return {success: false, message: `The sql ${dbServer[0].value} is not reachable`};
if (!serverUp) {
createLog(
"error",
"lst",
"server",
`The sql ${dbServer[0].value} is not reachable`
);
return {
success: false,
message: `The sql ${dbServer[0].value} is not reachable`,
};
}
if (!connected) {
createLog(
"error",
"lst",
"server",
`The sql ${dbServer[0].value} is not connected`
);
return {
success: false,
message: `The sql ${dbServer[0].value} is not not connected`,
};
}
/**
* We no longer need to send over the plant token change as we do it inside the query function.
*/
const plantToken = await db
.select()
.from(settings)
.where(eq(settings.name, "plantToken"));
const query = queryToRun.replaceAll("test1", plantToken[0].value);
try {
const result = await pool.request().query(query);
return result.recordset;
} catch (error: any) {
if (error.code === "ETIMEOUT") {
createLog(
"error",
"lst",
"sqlProd",
`${JSON.stringify(error)}, ${name} did not run due to a timeout.`
);
throw new Error(`${name} query did not run due to a timeout.`);
}
if (!connected) {
createLog("error", "lst", "server", `The sql ${dbServer[0].value} is not connected`);
return {success: false, message: `The sql ${dbServer[0].value} is not not connected`};
if (error.code === "EREQUEST") {
throw new Error(
`${name} encoutnered an error ${error.originalError.info.message}`
);
}
/**
* We no longer need to send over the plant token change as we do it inside the query function.
*/
const plantToken = await db.select().from(settings).where(eq(settings.name, "plantToken"));
const query = queryToRun.replaceAll("test1", plantToken[0].value);
try {
const result = await pool.request().query(query);
return result.recordset;
} catch (error: any) {
if (error.code === "ETIMEOUT") {
createLog("error", "lst", "sqlProd", `${JSON.stringify(error)}, ${name} did not run due to a timeout.`);
throw new Error(`${name} query did not run due to a timeout.`);
}
if (error.code === "EREQUEST") {
throw new Error(`${name} encoutnered an error ${error.originalError.info.message}`);
}
//console.log(error.originalError.info.message);
//EREQUEST
//throw new Error(`${name} encoutnered an error ${error.code}`);
}
//console.log(error.originalError.info.message);
//EREQUEST
//throw new Error(`${name} encoutnered an error ${error.code}`);
}
}

View File

@@ -1,10 +1,11 @@
import type {Roles} from "./roles.js";
import type { Roles } from "./roles.js";
export type User = {
user_id?: string;
email?: string;
username?: string;
roles?: Roles[];
role?: string;
prod?: string;
user_id?: string;
email?: string;
username?: string;
roles?: Roles[];
role?: string;
prod?: string;
password?: string;
};

View File

@@ -1,20 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "nodenext",
"strict": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"types": ["node"],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"outDir": "./dist",
"removeComments": true,
"allowJs": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"exclude": ["node_modules", "frontend", "dist", "testFiles"]
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "nodenext",
"strict": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"types": ["node"],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"outDir": "./dist",
"removeComments": true,
"allowJs": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["server", "scripts/**/*.ts"],
"exclude": ["node_modules", "frontend", "dist", "testFiles"]
}