Compare commits

...

138 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
2e5de34cb5 fix(login): if we have a wrong password or username we didnt properly error instead we crashed 2025-03-21 19:44:02 -05:00
07e47e64ae fix(consume material): when we consumed material the button was never reenabled
closes #15
2025-03-21 19:42:51 -05:00
73eb70538e test(streaming logs): test for streaming logs 2025-03-21 13:36:12 -05:00
8b8c9acb69 refactor(serverdata): remapped the server list to all be on the E drive and deactivated
deactivated so i can double check we have everything per server now
2025-03-21 13:35:51 -05:00
cb3ab668d8 chore(updatescript): added in so we can do a full install with an env creation for old 2025-03-21 13:34:46 -05:00
f6654067f5 chore: bump build number to 47 2025-03-21 12:07:02 -05:00
532a722763 chore: bump build number to 46 2025-03-20 14:26:41 -05:00
38d1043606 chore: bump build number to 45 2025-03-20 14:09:36 -05:00
18daca904e docs(logs): changes how logs are put into the db they will be there name vs key 2025-03-20 14:07:37 -05:00
e833c48cc8 feat(logger): streaming logs works server side not frontend for now 2025-03-20 14:06:53 -05:00
0bd217c727 docs(migration): added more documents on the install md 2025-03-20 14:04:48 -05:00
74974323f0 feat(lst): added in delay global function 2025-03-20 14:03:09 -05:00
f3c4c26ef9 test(ocp dash): more work on the dashboard 2025-03-20 14:02:46 -05:00
74bcd6e805 feat(ocme): cycle count implemeneted 2025-03-20 14:02:19 -05:00
eb2c34c557 ci(frontend): added ocme as a proxy in the dev 2025-03-20 14:00:42 -05:00
808e3d84ef chore: bump build number to 44 2025-03-20 09:51:25 -05:00
7a22b52c91 test(ocme): cycle count intital improvements 2025-03-19 21:45:10 -05:00
e17b8e7bbe refactor(view access): if role [] then allow them to see it 2025-03-19 17:59:22 -05:00
e597968777 feat(settings): more seed settings 2025-03-19 17:18:13 -05:00
bbd7a17144 feat(servers): added dayton in 2025-03-19 17:17:54 -05:00
5945ace9f2 refactor(settings): removed the need to login to get the settings 2025-03-19 17:17:36 -05:00
316b27e3e0 refactor(settings): used the common response function created 2025-03-19 17:17:18 -05:00
7165c959b9 feat(ocp): added in service plus manual print log 2025-03-19 17:16:39 -05:00
ae7e3fd54e feat(ocme): added in ocme service so we can utlize 2 ports 2025-03-19 17:16:14 -05:00
7ec5c5beb0 feat(logger): logger service created with its endpoints 2025-03-19 17:15:33 -05:00
c9aa41ab00 refactor(login): removed roles from the login to shrink the jwt 2025-03-19 17:15:02 -05:00
4696835c65 feat(installer): added a check for lstv2 already installed 2025-03-19 17:14:33 -05:00
2d3f308877 feat(server): ocpService and loggerService added 2025-03-19 17:13:52 -05:00
3d083986ae feat(admincheck): this check is so we dont use stuff on the wrong servers 2025-03-19 17:13:21 -05:00
92b47f03d9 style(types): added in prod? so we dont get type errors 2025-03-19 17:12:51 -05:00
3b8f18093e refactor(stores): added in axios 2025-03-19 17:12:27 -05:00
1cd1d3a3e9 feat(settings): added in setting store 2025-03-19 17:12:09 -05:00
8324fffeb6 test(ocp): more work on the dashboard 2025-03-19 17:11:00 -05:00
354f3260a5 refactor(consume materail): get token from localstorage as the store isnt wokring properly 2025-03-19 17:09:57 -05:00
ab5af4deac refactor(production): changes ocp to viewwer 2025-03-19 17:09:16 -05:00
7a15b160ac refactor(auth): moved prod back to server as we run 2 instances during migration 2025-03-19 17:08:51 -05:00
ca0ba7fe59 refactor(settings): refactored the admincheck so we can reuse it 2025-03-19 17:08:22 -05:00
0914b53341 feat(db): logs, manualprints added 2025-03-19 17:07:37 -05:00
34b80cf236 chore: bump build number to 43 2025-03-19 16:18:04 -05:00
196ea00972 chore: bump build number to 42 2025-03-19 13:30:10 -05:00
807a4ca699 chore: bump build number to 41 2025-03-19 11:32:58 -05:00
d98a659262 chore: bump build number to 40 2025-03-19 11:23:35 -05:00
6dd5f4b61f chore: bump build number to 39 2025-03-19 11:22:04 -05:00
751b9d5701 chore: bump build number to 38 2025-03-19 08:47:27 -05:00
b0634d9427 chore: bump build number to 37 2025-03-19 08:41:27 -05:00
8a143fbb19 chore: bump build number to 36 2025-03-19 08:23:56 -05:00
8b72a1b47e chore: bump build number to 35 2025-03-19 08:09:14 -05:00
f4c44fb02b chore: bump build number to 34 2025-03-18 22:29:52 -05:00
03aa7e5aee chore: bump build number to 33 2025-03-18 22:07:51 -05:00
f035e6f14a chore: bump build number to 32 2025-03-18 22:01:10 -05:00
227e2aa00c chore: bump build number to 31 2025-03-18 21:53:44 -05:00
4a48dd2bb5 chore: bump build number to 30 2025-03-18 18:37:43 -05:00
9796947db5 chore: bump build number to 29 2025-03-18 18:03:39 -05:00
1e02d4fa4f chore: bump build number to 28 2025-03-18 17:58:04 -05:00
26ea8d5e89 chore: bump build number to 27 2025-03-18 16:46:08 -05:00
8fb06c71d3 feat(auth): remove all old localstorage if no session 2025-03-17 16:16:06 -05:00
e258aaead9 feat(ocmeserver): the server was just migrated so it can be upgraded to lstv2 2025-03-17 16:13:58 -05:00
491de26a0b chore: bump build number to 26 2025-03-17 13:15:57 -05:00
f1979f0fc9 chore: bump build number to 25 2025-03-17 11:55:00 -05:00
f4433f4192 chore: bump build number to 24 2025-03-17 10:26:04 -05:00
bff0e77766 chore: bump build number to 23 2025-03-17 10:22:31 -05:00
43ca16dc80 chore: bump build number to 22 2025-03-17 08:21:54 -05:00
121bed59fd feat(ports): added in production port if in production 2025-03-17 08:21:09 -05:00
4e885ce74c chore: bump build number to 21 2025-03-17 08:11:25 -05:00
4908d6644a feat(serverdata): added in huston, sherman. and corrected contact info in westbend 2025-03-17 08:09:35 -05:00
21c374903b test(rfid): more work on the rfid service 2025-03-17 08:09:00 -05:00
ed11b2b26f fix(auth): added in the correct function for days between logins 2025-03-17 08:08:21 -05:00
357c118396 docs(install): removed the super secret key 2025-03-17 08:07:51 -05:00
866b6d5120 refactor(server query): bumped the refresh from 500ms to 2500ms 2025-03-17 08:07:30 -05:00
739e6bbe9f chore(builds): bummped lstv1 build 2025-03-17 08:07:04 -05:00
0975f4e499 chore: bump build number to 20 2025-03-16 15:38:31 -05:00
2990a330dd chore(release): 2.8.0 2025-03-16 15:38:06 -05:00
cb59f58926 feat(tag reading): more tag reading updates, with more contorl now
station 1 and 2 are still pending but didnt have enough tags at the office trial it
2025-03-16 15:37:40 -05:00
1a79a97929 refactor(rfid): cleaned up contorller folder 2025-03-16 15:36:43 -05:00
4b92a28dfa feat(rfid): no read console log for now but will show in frontend as well 2025-03-16 15:36:09 -05:00
4aae659ee4 feat(rfid): add/update readers now possible 2025-03-16 15:35:43 -05:00
7bfb48b81f feat(api): added in a response function to reduce the over responses as they are always the same 2025-03-16 15:34:55 -05:00
7432decd3c test(api): testing api options to reduce the code as we keep repeating same functions 2025-03-16 15:34:14 -05:00
04a607d3bc test(printers): started printer migration 2025-03-16 15:33:44 -05:00
d178e04362 refactor(rfid tags): update tag table to include what is needed and changed columns to be correct 2025-03-16 15:33:20 -05:00
020fdc83af refactor(reader): added in missing columns needed in the table 2025-03-16 15:32:47 -05:00
33803a69a6 feat(rfid): reader and tag db completed 2025-03-16 15:32:21 -05:00
3ea6dc5bc4 chore: bump build number to 19 2025-03-15 15:35:56 -05:00
b484a0c5ea chore(release): 2.7.0 2025-03-15 15:35:30 -05:00
a73c63cefa fix(rfid): correction to the params and incorrect naming 2025-03-15 15:34:04 -05:00
5b97d078c5 feat(new setting): added in devDir ment for updating servers 2025-03-15 15:33:41 -05:00
7529cc5b0c feat(serverdata): added in bowling green 2 2025-03-15 15:33:04 -05:00
df252e72b3 feat(server): added in update server as well as get serverdata 2025-03-15 15:32:15 -05:00
359427824b refactor(frontend): removed tanstack devTools 2025-03-15 15:31:22 -05:00
cbdd218fe4 feat(frontend): added in proper links for settings and servers to the sidebar 2025-03-15 15:30:53 -05:00
35acd2b0b3 refactor(frontend): removed the caption from settings table 2025-03-15 15:30:19 -05:00
625d5969be feat(frontend): added in update server page only for sysAdmin 2025-03-15 15:29:53 -05:00
2370d45220 fix(frontend): if the modules returns and error we want to use an empty array 2025-03-15 15:29:25 -05:00
cb7a4068fc feat(sql query): added 2 catches if not connected dont run 2025-03-15 15:28:51 -05:00
e4d15ef051 feat(sql server): added in the ping check to not spam if we are not connected 2025-03-15 15:25:46 -05:00
f3fa617aa5 feat(serverdata): added catch if we are not on localhost we cant actaully see the devDir in set 2025-03-15 15:24:11 -05:00
ab16059387 feat(server upgrade): added in a catch incase we try to upgrade again 2025-03-15 15:22:38 -05:00
b6f1cfdc6c chore: bump build number to 18 2025-03-14 14:47:51 -05:00
1ce5f9acf7 chore: bump build number to 17 2025-03-14 14:42:36 -05:00
44da09d22c chore: bump build number to 16 2025-03-14 14:38:14 -05:00
0b72ffa935 chore: bump build number to 15 2025-03-14 14:35:19 -05:00
a2fb845e2e chore: bump build number to 14 2025-03-14 14:33:58 -05:00
bcd65b4b91 chore: bump build number to 13 2025-03-14 14:33:00 -05:00
b1d25c7ba2 chore: bump build number to 12 2025-03-14 14:31:11 -05:00
c9a5203131 chore: bump build number to 11 2025-03-14 14:08:07 -05:00
246b5a17bd fix(zippaths): corrected the paths to the src that were moved the the env 2025-03-14 13:46:11 -05:00
245ba19cdc chore: bump build number to 10 2025-03-14 13:45:25 -05:00
e5e6aa3fee chore(release): 2.6.0 2025-03-14 13:39:00 -05:00
173 changed files with 26015 additions and 13791 deletions

11
.gitignore vendored
View File

@@ -5,6 +5,11 @@ dist
apiDocsLSTV2
testFiles
builds
nssm.exe
backend-0.1.2-217.zip
backend-0.1.2-218.zip
backend-0.1.2.zip
postgresql-17.2-3-windows-x64.exe
# ---> Node
@@ -143,8 +148,4 @@ dist
.yarn/install-state.gz
.pnp.*
nssm.exe
backend-0.1.2-217.zip
postgresql-17.2-3-windows-x64.exe
backend-0.1.3.zip

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,218 @@
# All CHanges to LST can be found below.
## [2.9.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.8.0...v2.9.0) (2025-03-23)
### 📈 Project changes
* **frontend:** added ocme as a proxy in the dev ([eb2c34c](https://git.tuffraid.net/cowch/lstV2/commits/eb2c34c557b72c3387b70addac30a4d8291402d4))
### 📚 Documentation
* **install:** added more env params and an install install stuff ([09d3a70](https://git.tuffraid.net/cowch/lstV2/commits/09d3a7041a18283a5add86ea845a8d7249522769))
* **install:** removed the super secret key ([357c118](https://git.tuffraid.net/cowch/lstV2/commits/357c1183964be0f3c02207c24bc8f83347282211))
* **logs:** changes how logs are put into the db they will be there name vs key ([18daca9](https://git.tuffraid.net/cowch/lstV2/commits/18daca904e0305371c6128988b48d54e3aec9a2a))
* **migration:** added more documents on the install md ([0bd217c](https://git.tuffraid.net/cowch/lstV2/commits/0bd217c727d726d62ddf06a0e44e95c4606361cf))
### 📝 Testing Code
* **ocme:** cycle count intital improvements ([7a22b52](https://git.tuffraid.net/cowch/lstV2/commits/7a22b52c916335c748598b82e78819c325bb8c4c))
* **ocp dash:** more work on the dashboard ([f3c4c26](https://git.tuffraid.net/cowch/lstV2/commits/f3c4c26ef957e6fe359a77f139ad36cdacefb8ef))
* **ocp:** more work on the dashboard ([8324fff](https://git.tuffraid.net/cowch/lstV2/commits/8324fffeb664bff3751ffc26e988a4a506b658a9))
* **returnres:** tryed to make a standard return res but will come back to this later ([58b5842](https://git.tuffraid.net/cowch/lstV2/commits/58b58424abbf98feec0c5442a59f4d055bbd1811))
* **rfid:** more work on the rfid service ([21c3749](https://git.tuffraid.net/cowch/lstV2/commits/21c374903b5cba56ced44b7eb2066ee902fc7bc3))
* **server:** start/stop/restart buttons added. comment [#14](https://git.tuffraid.net/cowch/lstV2/issues/14) ([76bc0db](https://git.tuffraid.net/cowch/lstV2/commits/76bc0db3dd9c48b69e4f6f571181a1f07c48bdeb))
* **streaming logs:** more test but failed again ([08c9b34](https://git.tuffraid.net/cowch/lstV2/commits/08c9b3404f548a167464445ec2f19288a092b0dc))
* **streaming logs:** test for streaming logs ([73eb705](https://git.tuffraid.net/cowch/lstV2/commits/73eb70538e36601cf436aaba244f99d5bd873d34))
* **streaming logs:** will come back to this later this is killing me inside ([97eb73c](https://git.tuffraid.net/cowch/lstV2/commits/97eb73c6d1aef747ef98f5f666fe47622795746f))
* **streaming:** more streaming test ([4db4eea](https://git.tuffraid.net/cowch/lstV2/commits/4db4eea2d12e37809879e96ed4279d6e23e83acc))
* **streaming:** more testing on streaming the lofs ([e82ef76](https://git.tuffraid.net/cowch/lstV2/commits/e82ef76316f0562b57124eb977c86fd2c6a3f332))
### 🌟 Enhancements
* **admincheck:** this check is so we dont use stuff on the wrong servers ([3d08398](https://git.tuffraid.net/cowch/lstV2/commits/3d083986aed528c2f881d6f673c0be03b8986c0b))
* **auth:** add, update were added for adm account in backend only ([d8eddaf](https://git.tuffraid.net/cowch/lstV2/commits/d8eddafcaea9141e9413a122a320649c7dd325e5))
* **auth:** remove all old localstorage if no session ([8fb06c7](https://git.tuffraid.net/cowch/lstV2/commits/8fb06c71d370a27697628077474a261596b567e0))
* **db:** logs, manualprints added ([0914b53](https://git.tuffraid.net/cowch/lstV2/commits/0914b5334119fa705eedddc6c9e8303cb82da551))
* **installer:** added a check for lstv2 already installed ([4696835](https://git.tuffraid.net/cowch/lstV2/commits/4696835c6557c2eae3be6a292144493be9d98f67))
* **logger:** logger service created with its endpoints ([7ec5c5b](https://git.tuffraid.net/cowch/lstV2/commits/7ec5c5beb0a9b02e8b8761908ee75ac2cfd317ee))
* **logger:** streaming logs works server side not frontend for now ([e833c48](https://git.tuffraid.net/cowch/lstV2/commits/e833c48cc8f68af40888ea161100e02f6ad19604))
* **lst:** added in delay global function ([7497432](https://git.tuffraid.net/cowch/lstV2/commits/74974323f0a7bd7c2eaa22024b7a8e91db2868e2))
* **lst:** added prettier config so we have the same formatting across all computers ([132e8d0](https://git.tuffraid.net/cowch/lstV2/commits/132e8d0146318fa9deb673bbcabf0d80c2a6d39f))
* **ocme:** added in ocme service so we can utlize 2 ports ([ae7e3fd](https://git.tuffraid.net/cowch/lstV2/commits/ae7e3fd54e5c256fb82e68f2e935e3b914e43d13))
* **ocme:** cycle count implemeneted ([74bcd6e](https://git.tuffraid.net/cowch/lstV2/commits/74bcd6e805c34e181f0c48310a799daf3afaee66))
* **ocmeserver:** the server was just migrated so it can be upgraded to lstv2 ([e258aae](https://git.tuffraid.net/cowch/lstV2/commits/e258aaead9a56cc4de36c17ed60b0bdaa91019cb))
* **ocp:** added in service plus manual print log ([7165c95](https://git.tuffraid.net/cowch/lstV2/commits/7165c959b9d5b37c0d4b01cb3f5bc27b40cec71d))
* **ports:** added in production port if in production ([121bed5](https://git.tuffraid.net/cowch/lstV2/commits/121bed59fda04b2ad134feb0895428515a1c56d8))
* **scripts:** made moving scripts more proper ([d0a0d08](https://git.tuffraid.net/cowch/lstV2/commits/d0a0d0890255acf890ba402386847b19566c92df))
* **serverdata:** added in huston, sherman. and corrected contact info in westbend ([4908d66](https://git.tuffraid.net/cowch/lstV2/commits/4908d6644a392395e660d382536c0fd31e3d0647))
* **server:** ocpService and loggerService added ([2d3f308](https://git.tuffraid.net/cowch/lstV2/commits/2d3f30887744bddfe22dc764188cf727a5e476df))
* **servers:** added dayton in ([bbd7a17](https://git.tuffraid.net/cowch/lstV2/commits/bbd7a17144e1e1a0faa192139cb1539c2f1ecc5c))
* **settings:** added in setting store ([1cd1d3a](https://git.tuffraid.net/cowch/lstV2/commits/1cd1d3a3e9e1ec1bf16ac2552caf24aa299959bf))
* **settings:** more seed settings ([e597968](https://git.tuffraid.net/cowch/lstV2/commits/e597968777e88289d361fdc01ec683a1f4c80192))
* **trycatch:** added in theo's try catch to reduce the code and love it ([72d52d9](https://git.tuffraid.net/cowch/lstV2/commits/72d52d925677eeeac6d158028114201862b6e2a2))
### 🛠️ Code Refactor
* **auth:** added in correct bycrptjs ([208cd61](https://git.tuffraid.net/cowch/lstV2/commits/208cd615af8250686745aa5b6779706ae267e423))
* **auth:** moved prod back to server as we run 2 instances during migration ([7a15b16](https://git.tuffraid.net/cowch/lstV2/commits/7a15b160ac2393cd66a932e598ffaa5aeda5812f))
* **consume materail:** get token from localstorage as the store isnt wokring properly ([354f326](https://git.tuffraid.net/cowch/lstV2/commits/354f3260a55b53bfb461a37db9d64550bacbfe04))
* **frontend:** added date-fns into the frontend ([bba0aa2](https://git.tuffraid.net/cowch/lstV2/commits/bba0aa2ee4e9b9be1db184da894ce6e96fd2e38f))
* **login:** removed all the data from teh login dropdown as it could cause issues ([5dfece0](https://git.tuffraid.net/cowch/lstV2/commits/5dfece09b7285dda96875bbc740df803d816b92a))
* **login:** removed roles from the login to shrink the jwt ([c9aa41a](https://git.tuffraid.net/cowch/lstV2/commits/c9aa41ab0099b7a05d50d9a981cf7e8a42a04733))
* **production:** changes ocp to viewwer ([ab5af4d](https://git.tuffraid.net/cowch/lstV2/commits/ab5af4deacbeaf1ed93c6231fb98b187f7540ca4))
* **server query:** bumped the refresh from 500ms to 2500ms ([866b6d5](https://git.tuffraid.net/cowch/lstV2/commits/866b6d5120810252b089580d341f3fb1b62e951a))
* **serverdata:** remapped the server list to all be on the E drive and deactivated ([8b8c9ac](https://git.tuffraid.net/cowch/lstV2/commits/8b8c9acb6969b63f157ea95b7d61923bf4bb4eae))
* **server:** removed the websocket wrapper going wiht normal ws ([bb6d523](https://git.tuffraid.net/cowch/lstV2/commits/bb6d523abbd3c9423eddbaab96e297b4850e2aa8))
* **settings:** refactored the admincheck so we can reuse it ([ca0ba7f](https://git.tuffraid.net/cowch/lstV2/commits/ca0ba7fe59f7e19d0de4924a419cce461de4b7a1))
* **settings:** removed the need to login to get the settings ([5945ace](https://git.tuffraid.net/cowch/lstV2/commits/5945ace9f259f6ef418c1621c246982b5b572dc1))
* **settings:** used the common response function created ([316b27e](https://git.tuffraid.net/cowch/lstV2/commits/316b27e3e011a0c0b4ce88ea579290807b8927c5))
* **stores:** added in axios ([3b8f180](https://git.tuffraid.net/cowch/lstV2/commits/3b8f18093ead2a988b2b19d1e8f25db6eaeaaee8))
* **view access:** if role [] then allow them to see it ([e17b8e7](https://git.tuffraid.net/cowch/lstV2/commits/e17b8e7bbe94c25c1bd3414b1db31191c87553d6))
### 🐛 Bug fixes
* **auth:** added in the correct function for days between logins ([ed11b2b](https://git.tuffraid.net/cowch/lstV2/commits/ed11b2b26ff80fde9f94615f740eb5152b16744d))
* **auth:** fixed the getaccess to be getuseraccess as it was orignally ([b9dd6e3](https://git.tuffraid.net/cowch/lstV2/commits/b9dd6e3ae2f5c158dd4827ae0c9db2c6747c57e1))
* **calendar:** this component had a bug and needed a lib update ([93ed2e9](https://git.tuffraid.net/cowch/lstV2/commits/93ed2e9ee8c98edd6a8c47d6e7a0caf6a8e93278))
* **consume material:** when we consumed material the button was never reenabled ([07e47e6](https://git.tuffraid.net/cowch/lstV2/commits/07e47e64ae2f4ddd325a2fdb34c82143c9adf84b)), closes [#15](https://git.tuffraid.net/cowch/lstV2/issues/15)
* **frontend:** removed unwanted import ([10d88f5](https://git.tuffraid.net/cowch/lstV2/commits/10d88f53ebaf670c9f6b3ca59cbbaf4d88f26b9f))
* **loginform:** removed the console log that was left by accident ([2ae3c8b](https://git.tuffraid.net/cowch/lstV2/commits/2ae3c8ba5916b5249135b86b374ac1bf32837478))
* **login:** if we have a wrong password or username we didnt properly error instead we crashed ([2e5de34](https://git.tuffraid.net/cowch/lstV2/commits/2e5de34cb50c79cd60e038492b2397eee69def11))
* **scaler:** fix due to update ([edcfff6](https://git.tuffraid.net/cowch/lstV2/commits/edcfff6cc6cf81e56b5532f0876383659b951a5d))
* **serverlist:** corrected the time by removing teh Z at the end of the time ([f940bcd](https://git.tuffraid.net/cowch/lstV2/commits/f940bcdc9df2d3e9989d49feae88036a6f8c7013))
* **sqlserver:** if we already have a connection just return we dont want to try a second time ([b7773ec](https://git.tuffraid.net/cowch/lstV2/commits/b7773ec02aa26c01530723dc146e0c9f4dae41d3))
### 📝 Chore
* **builds:** bummped lstv1 build ([739e6bb](https://git.tuffraid.net/cowch/lstV2/commits/739e6bbe9fa48fae682f9931a2c8bcb763feb636))
* bump build number to 20 ([0975f4e](https://git.tuffraid.net/cowch/lstV2/commits/0975f4e499a8a2aec0662352cb4496299292d4ea))
* bump build number to 21 ([4e885ce](https://git.tuffraid.net/cowch/lstV2/commits/4e885ce74c02ee1fd95a93402d1d32ecf357b1cb))
* bump build number to 22 ([43ca16d](https://git.tuffraid.net/cowch/lstV2/commits/43ca16dc807b9fa3496498e90a871568f0e5f54c))
* bump build number to 23 ([bff0e77](https://git.tuffraid.net/cowch/lstV2/commits/bff0e77766b49c72d77fa55cfaab2d347a1a75dc))
* bump build number to 24 ([f4433f4](https://git.tuffraid.net/cowch/lstV2/commits/f4433f41926dab72a239fc585ddd1e14acb80c7e))
* bump build number to 25 ([f1979f0](https://git.tuffraid.net/cowch/lstV2/commits/f1979f0fc914f3b94bad1816cd405a6431d1ba4a))
* bump build number to 26 ([491de26](https://git.tuffraid.net/cowch/lstV2/commits/491de26a0bbe6dfca9e1cb068991789c29a09ac0))
* bump build number to 27 ([26ea8d5](https://git.tuffraid.net/cowch/lstV2/commits/26ea8d5e89bb1b44ceb6b205da3ad158c603fca0))
* bump build number to 28 ([1e02d4f](https://git.tuffraid.net/cowch/lstV2/commits/1e02d4fa4fc1a007634da49fe2697d63c89cba18))
* bump build number to 29 ([9796947](https://git.tuffraid.net/cowch/lstV2/commits/9796947db5443c8b5678d7502037458d680e4018))
* bump build number to 30 ([4a48dd2](https://git.tuffraid.net/cowch/lstV2/commits/4a48dd2bb57064953ef6e192c76b91bb844a24de))
* bump build number to 31 ([227e2aa](https://git.tuffraid.net/cowch/lstV2/commits/227e2aa00c2a3e5526e0347c76676de345dfea5d))
* bump build number to 32 ([f035e6f](https://git.tuffraid.net/cowch/lstV2/commits/f035e6f14a9e2123a10010d4f515f78f0c222599))
* bump build number to 33 ([03aa7e5](https://git.tuffraid.net/cowch/lstV2/commits/03aa7e5aeee39b1b390b4da2019f44459487e0a0))
* bump build number to 34 ([f4c44fb](https://git.tuffraid.net/cowch/lstV2/commits/f4c44fb02ba857890af66a2747381ef1c3def25b))
* bump build number to 35 ([8b72a1b](https://git.tuffraid.net/cowch/lstV2/commits/8b72a1b47e715f4146e586e9fdd6abcabd97743d))
* bump build number to 36 ([8a143fb](https://git.tuffraid.net/cowch/lstV2/commits/8a143fbb19d2270b0139a7a4edbce745c477b6b1))
* bump build number to 37 ([b0634d9](https://git.tuffraid.net/cowch/lstV2/commits/b0634d9427f6fff3a239680204b5e6daf6163e37))
* bump build number to 38 ([751b9d5](https://git.tuffraid.net/cowch/lstV2/commits/751b9d5701cf7e81641f154a02041869e1b80c49))
* bump build number to 39 ([6dd5f4b](https://git.tuffraid.net/cowch/lstV2/commits/6dd5f4b61f3a27779980cf14915c4394256eae20))
* bump build number to 40 ([d98a659](https://git.tuffraid.net/cowch/lstV2/commits/d98a6592628f36c654039ccea25db163ddf15e8c))
* bump build number to 41 ([807a4ca](https://git.tuffraid.net/cowch/lstV2/commits/807a4ca6993b5dfa82a45aaf44a2d661c3e03428))
* bump build number to 42 ([196ea00](https://git.tuffraid.net/cowch/lstV2/commits/196ea009720a65c520f5626658ac7885a41796cb))
* bump build number to 43 ([34b80cf](https://git.tuffraid.net/cowch/lstV2/commits/34b80cf2368228b70cd6a79d44ba0f95b5c5941a))
* bump build number to 44 ([808e3d8](https://git.tuffraid.net/cowch/lstV2/commits/808e3d84efa0889077a12cbf8e28df794a51f2bc))
* bump build number to 45 ([38d1043](https://git.tuffraid.net/cowch/lstV2/commits/38d10436069d3db1612a622d9b9e0c7aec04f6dd))
* bump build number to 46 ([532a722](https://git.tuffraid.net/cowch/lstV2/commits/532a7227631cf67664ca41133330d8cfa59f429b))
* bump build number to 47 ([f665406](https://git.tuffraid.net/cowch/lstV2/commits/f6654067f5f01c3ee4855f0cbe07593f13263ce7))
* bump build number to 48 ([97b9c4d](https://git.tuffraid.net/cowch/lstV2/commits/97b9c4db4a67faf68edae4dc1217b28f87d003e8))
* bump build number to 49 ([acb9876](https://git.tuffraid.net/cowch/lstV2/commits/acb9876d9b715894481d31d9bb104e54561710d0))
* **pkg updates:** updated all pkgs ([fb9ee15](https://git.tuffraid.net/cowch/lstV2/commits/fb9ee15bda89257815012023ed5543ef55b5f379))
* **updatescript:** added in so we can do a full install with an env creation for old ([cb3ab66](https://git.tuffraid.net/cowch/lstV2/commits/cb3ab668d866ea9e3428e548d9065a9681174e82))
## [2.8.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.7.0...v2.8.0) (2025-03-16)
### 📝 Chore
* bump build number to 19 ([3ea6dc5](https://git.tuffraid.net/cowch/lstV2/commits/3ea6dc5bc46b6a060548566ea606095d0e30c96c))
### 📝 Testing Code
* **api:** testing api options to reduce the code as we keep repeating same functions ([7432dec](https://git.tuffraid.net/cowch/lstV2/commits/7432decd3ce9033ad310850fe5b0cbba076526a5))
* **printers:** started printer migration ([04a607d](https://git.tuffraid.net/cowch/lstV2/commits/04a607d3bcf2020624e45ba5ddf83f0b13acbbc7))
### 🛠️ Code Refactor
* **reader:** added in missing columns needed in the table ([020fdc8](https://git.tuffraid.net/cowch/lstV2/commits/020fdc83af2b5e90b76fa43b884965f82b5db466))
* **rfid tags:** update tag table to include what is needed and changed columns to be correct ([d178e04](https://git.tuffraid.net/cowch/lstV2/commits/d178e0436247d83b3e33dc7c92bb49aa023b6198))
* **rfid:** cleaned up contorller folder ([1a79a97](https://git.tuffraid.net/cowch/lstV2/commits/1a79a9792913bb984f230f29fede1bfa6da2991f))
### 🌟 Enhancements
* **api:** added in a response function to reduce the over responses as they are always the same ([7bfb48b](https://git.tuffraid.net/cowch/lstV2/commits/7bfb48b81fcfe759e8f448db30db93b1c320de1a))
* **rfid:** add/update readers now possible ([4aae659](https://git.tuffraid.net/cowch/lstV2/commits/4aae659ee41d952e823ba4582b4d3fab05bd2500))
* **rfid:** no read console log for now but will show in frontend as well ([4b92a28](https://git.tuffraid.net/cowch/lstV2/commits/4b92a28dfa3db011a451aff913175ff8d21feb1b))
* **rfid:** reader and tag db completed ([33803a6](https://git.tuffraid.net/cowch/lstV2/commits/33803a69a6b9b0d24297f7a9d463d16dbae4ca44))
* **tag reading:** more tag reading updates, with more contorl now ([cb59f58](https://git.tuffraid.net/cowch/lstV2/commits/cb59f589264583b2ee6d8f0255d3ceb33f101a01))
## [2.7.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.6.0...v2.7.0) (2025-03-15)
### 📝 Chore
* bump build number to 10 ([245ba19](https://git.tuffraid.net/cowch/lstV2/commits/245ba19cdc7e67fec9343bb9eba90287d8951854))
* bump build number to 11 ([c9a5203](https://git.tuffraid.net/cowch/lstV2/commits/c9a520313160959ed929351f55bd18606b6500a9))
* bump build number to 12 ([b1d25c7](https://git.tuffraid.net/cowch/lstV2/commits/b1d25c7ba27a72c42803a3b482234c41670a07a2))
* bump build number to 13 ([bcd65b4](https://git.tuffraid.net/cowch/lstV2/commits/bcd65b4b91cf6cb24a32323ead5ff6a73d3c9d3d))
* bump build number to 14 ([a2fb845](https://git.tuffraid.net/cowch/lstV2/commits/a2fb845e2efbeb8becf91082343770b2d730330c))
* bump build number to 15 ([0b72ffa](https://git.tuffraid.net/cowch/lstV2/commits/0b72ffa935be81476a15166fd60ae8cabe29d297))
* bump build number to 16 ([44da09d](https://git.tuffraid.net/cowch/lstV2/commits/44da09d22c4d946c981d408d797ea121d26690b6))
* bump build number to 17 ([1ce5f9a](https://git.tuffraid.net/cowch/lstV2/commits/1ce5f9acf752f5f4bcb4b2ff1c71f54ffb9331f3))
* bump build number to 18 ([b6f1cfd](https://git.tuffraid.net/cowch/lstV2/commits/b6f1cfdc6c71e351fb90ec7729e0d0f89322174f))
### 🛠️ Code Refactor
* **frontend:** removed tanstack devTools ([3594278](https://git.tuffraid.net/cowch/lstV2/commits/359427824bae319b7f2f406eb52b8f9c43be198f))
* **frontend:** removed the caption from settings table ([35acd2b](https://git.tuffraid.net/cowch/lstV2/commits/35acd2b0b3af7b9d010cb8f78f088ab3a539c54b))
### 🌟 Enhancements
* **frontend:** added in proper links for settings and servers to the sidebar ([cbdd218](https://git.tuffraid.net/cowch/lstV2/commits/cbdd218fe454e38a7cf0c4d0ddf60d1f20e15ee2))
* **frontend:** added in update server page only for sysAdmin ([625d596](https://git.tuffraid.net/cowch/lstV2/commits/625d5969be2dbde9e97f6607c33c5e5b14e3d192))
* **new setting:** added in devDir ment for updating servers ([5b97d07](https://git.tuffraid.net/cowch/lstV2/commits/5b97d078c583f3e78d52dbc135da99a0175c8e54))
* **server upgrade:** added in a catch incase we try to upgrade again ([ab16059](https://git.tuffraid.net/cowch/lstV2/commits/ab16059387b7cb46e2a3d86f6da09a31899bd5d6))
* **server:** added in update server as well as get serverdata ([df252e7](https://git.tuffraid.net/cowch/lstV2/commits/df252e72b39d811ffbbb0af49aaf43ff22081f48))
* **serverdata:** added catch if we are not on localhost we cant actaully see the devDir in set ([f3fa617](https://git.tuffraid.net/cowch/lstV2/commits/f3fa617aa53ed35f461b71f5e479ea521412936e))
* **serverdata:** added in bowling green 2 ([7529cc5](https://git.tuffraid.net/cowch/lstV2/commits/7529cc5b0cc2c6542e8f4af07d465b6a9f4295b1))
* **sql query:** added 2 catches if not connected dont run ([cb7a406](https://git.tuffraid.net/cowch/lstV2/commits/cb7a4068fcffc7a1d85c2e04f2eeaebdc264705c))
* **sql server:** added in the ping check to not spam if we are not connected ([e4d15ef](https://git.tuffraid.net/cowch/lstV2/commits/e4d15ef051f6c72d2cca9bb9fb61ff78849db8c4))
### 🐛 Bug fixes
* **frontend:** if the modules returns and error we want to use an empty array ([2370d45](https://git.tuffraid.net/cowch/lstV2/commits/2370d45220c5e1c3215a3f2fce9582d8f2bbd3ed))
* **rfid:** correction to the params and incorrect naming ([a73c63c](https://git.tuffraid.net/cowch/lstV2/commits/a73c63cefa67f43300f4695f40f2248bcab8f40e))
* **zippaths:** corrected the paths to the src that were moved the the env ([246b5a1](https://git.tuffraid.net/cowch/lstV2/commits/246b5a17bd4ffe47654416fa4a1be446f8bffbd0))
## [2.6.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.5.1...v2.6.0) (2025-03-14)
### 📝 Chore
* bump build number to 9 ([542c08f](https://git.tuffraid.net/cowch/lstV2/commits/542c08f10ba590e7174864c7970fd0ac03ffebf9))
### 🛠️ Code Refactor
* **pkg:** removed dir stuff and moved to env ([242494a](https://git.tuffraid.net/cowch/lstV2/commits/242494a4eaaca0aaf12e9a309e5611e17ac9ccb0))
### 🌟 Enhancements
* **app update:** finished the app build / deploy functions and a scripts migration ([81eb10e](https://git.tuffraid.net/cowch/lstV2/commits/81eb10e021fd29e33ee3f7393439c72427df9561))
* **pkgdata:** moved the pkg info to a global function ([51b81b9](https://git.tuffraid.net/cowch/lstV2/commits/51b81b97bc4c4b112b128818d203deba49fb5011))
### [2.5.1](https://git.tuffraid.net/cowch/lstV2/compare/v2.5.0...v2.5.1) (2025-03-14)

View File

@@ -0,0 +1 @@
ALTER TABLE "serverData" ADD COLUMN "isUpgrading" boolean DEFAULT false;

View File

@@ -0,0 +1 @@
ALTER TABLE "rfidReaders" ADD COLUMN "lastTrigger" timestamp DEFAULT now();

View File

@@ -0,0 +1 @@
ALTER TABLE "rfidReaders" ADD COLUMN "active" boolean DEFAULT true;

View File

@@ -0,0 +1,2 @@
ALTER TABLE "rfidTags" ADD COLUMN "antenna" numeric;--> statement-breakpoint
ALTER TABLE "rfidTags" ADD COLUMN "tagStrength" numeric;

View File

@@ -0,0 +1,2 @@
ALTER TABLE "rfidTags" ALTER COLUMN "counts" SET DEFAULT '[]'::jsonb;--> statement-breakpoint
ALTER TABLE "rfidTags" ALTER COLUMN "counts" DROP NOT NULL;

View File

@@ -0,0 +1,4 @@
ALTER TABLE "rfidTags" ALTER COLUMN "runningNumber" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "rfidTags" ALTER COLUMN "runningNumber" DROP NOT NULL;--> statement-breakpoint
ALTER TABLE "rfidTags" ALTER COLUMN "antenna" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "rfidTags" ALTER COLUMN "tagStrength" SET DATA TYPE integer;

View File

@@ -0,0 +1 @@
ALTER TABLE "rfidReaders" ADD COLUMN "lastTiggerGood" boolean DEFAULT true;

View File

@@ -0,0 +1,2 @@
ALTER TABLE "logs" ADD COLUMN "checked" boolean DEFAULT false;--> statement-breakpoint
ALTER TABLE "logs" ADD COLUMN "checkedAt" timestamp;

View File

@@ -0,0 +1,9 @@
CREATE TABLE "manualPrinting" (
"print_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"line" integer,
"printReason" text NOT NULL,
"initials" text NOT NULL,
"additionalComments" text NOT NULL,
"add_date" timestamp DEFAULT now(),
"add_user" text
);

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -113,6 +113,76 @@
"when": 1741952150913,
"tag": "0015_wonderful_lady_vermin",
"breakpoints": true
},
{
"idx": 16,
"version": "7",
"when": 1742051878587,
"tag": "0016_wet_doctor_strange",
"breakpoints": true
},
{
"idx": 17,
"version": "7",
"when": 1742133439717,
"tag": "0017_bitter_brood",
"breakpoints": true
},
{
"idx": 18,
"version": "7",
"when": 1742133828291,
"tag": "0018_lovely_landau",
"breakpoints": true
},
{
"idx": 19,
"version": "7",
"when": 1742139737945,
"tag": "0019_greedy_justice",
"breakpoints": true
},
{
"idx": 20,
"version": "7",
"when": 1742143798267,
"tag": "0020_empty_thundra",
"breakpoints": true
},
{
"idx": 21,
"version": "7",
"when": 1742144973347,
"tag": "0021_premium_albert_cleary",
"breakpoints": true
},
{
"idx": 22,
"version": "7",
"when": 1742156466912,
"tag": "0022_amused_true_believers",
"breakpoints": true
},
{
"idx": 23,
"version": "7",
"when": 1742346003832,
"tag": "0023_wealthy_marvel_boy",
"breakpoints": true
},
{
"idx": 24,
"version": "7",
"when": 1742408812383,
"tag": "0024_curved_venom",
"breakpoints": true
},
{
"idx": 25,
"version": "7",
"when": 1742655504936,
"tag": "0025_amusing_sugar_man",
"breakpoints": true
}
]
}

View File

@@ -10,6 +10,8 @@ export const logs = pgTable(
username: text("username").default("LST_Serivce"),
service: text("service").notNull().default("system"),
message: text("message").notNull(),
checked: boolean("checked").default(false),
checkedAt: timestamp("checkedAt"),
created_at: timestamp("add_Date").defaultNow(),
},
(table) => [

View File

@@ -0,0 +1,20 @@
import {text, pgTable, timestamp, uuid, integer} from "drizzle-orm/pg-core";
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
import {z} from "zod";
export const manualPrinting = pgTable("manualPrinting", {
print_id: uuid("print_id").defaultRandom().primaryKey(),
line: integer("line"),
printReason: text("printReason").notNull(),
initials: text("initials").notNull(),
additionalComments: text("additionalComments").notNull(),
add_date: timestamp("add_date").defaultNow(),
add_user: text("add_user"),
});
// Schema for inserting a user - can be used to validate API requests
// export const insertRolesSchema = createInsertSchema(roles, {
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
// });
// Schema for selecting a Expenses - can be used to validate API responses
export const selectRolesSchema = createSelectSchema(manualPrinting);

View File

@@ -9,6 +9,9 @@ export const rfidReaders = pgTable(
reader: text("reader"),
readerIP: text("readerIP"),
lastHeartBeat: timestamp("lastHeartBeat").defaultNow(),
lastTrigger: timestamp("lastTrigger").defaultNow(),
lastTriggerGood: boolean("lastTiggerGood").default(true),
active: boolean("active").default(true),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),

View File

@@ -1,4 +1,4 @@
import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex, jsonb} from "drizzle-orm/pg-core";
import {text, pgTable, timestamp, uuid, uniqueIndex, jsonb, integer} from "drizzle-orm/pg-core";
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
import {z} from "zod";
@@ -9,9 +9,11 @@ export const rfidTags = pgTable(
tagHex: text("tagHex"),
tag: text("tag"),
lastRead: timestamp("timeStamp").defaultNow(),
counts: jsonb("counts").notNull(), //.default([{area: 1, timesHere: 5}]).notNull(),
counts: jsonb("counts").default([]), // example [{area: Line3.2, count: 1}, {area: line3.1, count: 6}]
lastareaIn: text("lastareaIn").notNull(),
runningNumber: numeric("runningNumber").notNull(),
runningNumber: integer("runningNumber"),
antenna: integer("antenna"),
tagStrength: integer("tagStrength"),
created_at: timestamp("created_at").defaultNow(),
},
(table) => [
@@ -21,8 +23,8 @@ export const rfidTags = pgTable(
);
// Schema for inserting a user - can be used to validate API requests
// export const insertRolesSchema = createInsertSchema(roles, {
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
// });
export const insertRolesSchema = createInsertSchema(rfidTags, {
tagHex: z.string().min(3, {message: "Tag Should have more than 3 characters"}),
});
// Schema for selecting a Expenses - can be used to validate API responses
export const selectRolesSchema = createSelectSchema(rfidTags);

View File

@@ -24,6 +24,7 @@ export const serverData = pgTable(
shippingHours: text("shippingHours").default('[{"early": "06:30", "late": "23:00"}]'),
tiPostTime: text("tiPostTime").default('[{"from": "24", "to": "24"}]'),
otherSettings: jsonb("otherSettings").default([{specialInstructions: "something for ti", active: false}]),
isUpgrading: boolean("isUpgrading").default(false),
},
(table) => [

File diff suppressed because it is too large Load Diff

View File

@@ -8,10 +8,11 @@
"build": "rimraf dist && tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"shad": "npx shadcn@canary add "
"shad": "npx shadcn@canary add ",
"checkupdates": "npm-check-updates"
},
"dependencies": {
"@hookform/resolvers": "^4.1.2",
"@hookform/resolvers": "^4.1.3",
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-collapsible": "^1.1.3",
@@ -19,49 +20,52 @@
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.8",
"@tailwindcss/vite": "^4.0.9",
"@tanstack/react-query": "^5.66.9",
"@tanstack/react-router": "^1.111.11",
"@tailwindcss/vite": "^4.0.15",
"@tanstack/react-query": "^5.69.0",
"@tanstack/react-router": "^1.114.27",
"@tanstack/react-table": "^8.21.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"date-fns-tz": "^3.2.0",
"dotenv": "^16.4.7",
"hono": "^4.7.2",
"hono": "^4.7.5",
"js-cookie": "^3.0.5",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.476.0",
"next-themes": "^0.4.4",
"lucide-react": "^0.483.0",
"next-themes": "^0.4.6",
"npm-check-updates": "^17.1.16",
"react": "^19.0.0",
"react-day-picker": "^8.10.1",
"react-dom": "^19.0.0",
"react-grid-layout": "^1.5.0",
"react-grid-layout": "^1.5.1",
"react-hook-form": "^7.54.2",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.9",
"tailwindcss": "^4.0.15",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2",
"zustand": "^5.0.3"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@tanstack/router-devtools": "^1.106.0",
"@tanstack/router-plugin": "^1.106.0",
"@types/react": "^19.0.10",
"@eslint/js": "^9.23.0",
"@tanstack/router-devtools": "^1.114.27",
"@tanstack/router-plugin": "^1.114.27",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"@types/react-grid-layout": "^1.3.5",
"@vitejs/plugin-react-swc": "^3.8.0",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.0.0",
"@vitejs/plugin-react-swc": "^3.8.1",
"eslint": "^9.23.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0"
"globals": "^16.0.0",
"typescript": "~5.8.2",
"typescript-eslint": "^8.27.0",
"vite": "^6.2.2"
}
}

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

@@ -0,0 +1,125 @@
import { LstCard } from "@/components/extendedUI/LstCard";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useSessionStore } from "@/lib/store/sessionStore";
import { useModuleStore } from "@/lib/store/useModuleStore";
import { getServers } from "@/utils/querys/servers";
import { useQuery } from "@tanstack/react-query";
import { useRouter } from "@tanstack/react-router";
import { format } from "date-fns";
import UpdateServer from "./UpdateServer";
import { adminUrlCheck } from "@/utils/adminUrlCheck";
import RestartServer from "./RestartServer";
import StopServer from "./StopServer";
import StartServer from "./StartServer";
export type Servers = {
server_id?: string;
sName?: string;
serverDNS?: string;
plantToken?: string;
idAddress: string;
lastUpdated: string;
isUpgrading: boolean;
};
export default function ServerPage() {
const { user, token } = useSessionStore();
const { modules } = useModuleStore();
const router = useRouter();
const { data, isError, error, isLoading } = useQuery(getServers(token ?? ""));
const adminModule = modules.filter((n) => n.name === "admin");
const userLevel =
user?.roles?.filter((r) => r.module_id === adminModule[0].module_id) || [];
if (!adminModule[0]?.roles?.includes(userLevel[0]?.role)) {
router.navigate({ to: "/" });
}
if (isError) {
return <div>{JSON.stringify(error)}</div>;
}
console.log(data);
return (
<LstCard className="m-2 flex place-content-center w-dvh">
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Server</TableHead>
<TableHead>PlantToken</TableHead>
<TableHead>IP Address</TableHead>
<TableHead>Date Last updated</TableHead>
<TableHead>Update Server</TableHead>
</TableRow>
</TableHeader>
{isLoading ? (
<>
<TableBody>
{Array(10)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</>
) : (
<TableBody>
{data?.map((server: Servers) => {
const strippedDate = server.lastUpdated.replace("Z", ""); // Remove Z
const formattedDate = format(strippedDate, "MM/dd/yyyy hh:mm a");
return (
<TableRow key={server.server_id}>
<TableCell className="font-medium">{server.sName}</TableCell>
<TableCell className="font-medium">
{server.serverDNS}
</TableCell>
<TableCell className="font-medium">
{server.plantToken}
</TableCell>
<TableCell className="font-medium">
{server.idAddress}
</TableCell>
<TableCell className="font-medium">{formattedDate}</TableCell>
<TableCell className="font-medium">
{adminUrlCheck() && (
<div className="flex flex-row">
<UpdateServer server={server} token={token as string} />
<StartServer />
<StopServer />
<RestartServer />
</div>
)}
</TableCell>
</TableRow>
);
})}
</TableBody>
)}
</Table>
</LstCard>
);
}

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

@@ -0,0 +1,51 @@
import {Button} from "@/components/ui/button";
import {CircleFadingArrowUp} from "lucide-react";
import {toast} from "sonner";
import {Servers} from "./ServerPage";
import {useQuery} from "@tanstack/react-query";
import {getSettings} from "@/utils/querys/settings";
import axios from "axios";
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
export default function UpdateServer({server, token}: {server: Servers; token: string}) {
const {data} = useQuery(getSettings(token ?? ""));
const upgrade = async () => {
let devDir = data.filter((n: any) => n.name === "devDir");
toast.success("Server being upgraded in the background please wait.");
try {
const result = await axios.post(
`/api/server/update/${server.plantToken}`,
{devDir: devDir[0].value},
{
headers: {Authorization: `Bearer ${token}`},
}
);
if (result.data.success) {
toast.success(result.data.message);
}
if (!result.data.success) {
toast.success(result.data.message);
}
} catch (error: any) {
toast.error(`There was an error updating the server: ${error.data.message}`);
}
};
return (
<div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant={"outline"} size={"icon"} onClick={upgrade} disabled={server.isUpgrading}>
<CircleFadingArrowUp />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Update {server.sName}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
}

View File

@@ -1,5 +1,5 @@
import {LstCard} from "@/components/extendedUI/LstCard";
import {Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {useSessionStore} from "@/lib/store/sessionStore";
import {useModuleStore} from "@/lib/store/useModuleStore";
import {useQuery} from "@tanstack/react-query";
@@ -37,9 +37,8 @@ export default function SettingsPage() {
}
return (
<LstCard className="m-2 flex place-content-center w-dvh">
<LstCard className="m-2 flex place-content-center w-fit">
<Table>
<TableCaption>All Settings</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>

View File

@@ -1,143 +1,164 @@
import {useSessionStore} from "../../lib/store/sessionStore";
import {LstCard} from "../extendedUI/LstCard";
import {CardHeader} from "../ui/card";
import {toast} from "sonner";
import {z} from "zod";
import {useRouter} from "@tanstack/react-router";
import {Controller, useForm} from "react-hook-form";
import {zodResolver} from "@hookform/resolvers/zod";
import {Label} from "../ui/label";
import {Input} from "../ui/input";
import {Checkbox} from "../ui/checkbox";
import {Button} from "../ui/button";
import { useSessionStore } from "../../lib/store/sessionStore";
import { LstCard } from "../extendedUI/LstCard";
import { CardHeader } from "../ui/card";
import { toast } from "sonner";
import { z } from "zod";
import { useRouter } from "@tanstack/react-router";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Label } from "../ui/label";
import { Input } from "../ui/input";
import { Checkbox } from "../ui/checkbox";
import { Button } from "../ui/button";
const FormSchema = z.object({
username: z.string().min(1, "You must enter a valid username"),
password: z.string().min(4, "You must enter a valid password"),
rememberMe: z.boolean(),
username: z.string().min(1, "You must enter a valid username"),
password: z.string().min(4, "You must enter a valid password"),
rememberMe: z.boolean(),
});
const LoginForm = () => {
const {setSession} = useSessionStore();
const rememeberMe = localStorage.getItem("rememberMe") === "true";
const username = localStorage.getItem("username") || "";
const router = useRouter();
const {
register,
handleSubmit,
control,
formState: {errors},
} = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
username: username || "",
password: "",
rememberMe: rememeberMe,
const { setSession } = useSessionStore();
const rememeberMe = localStorage.getItem("rememberMe") === "true";
const username = localStorage.getItem("username") || "";
const router = useRouter();
const {
register,
handleSubmit,
control,
formState: { errors },
} = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
username: username || "",
password: "",
rememberMe: rememeberMe,
},
});
const onSubmitLogin = async (value: z.infer<typeof FormSchema>) => {
// Do something with form data
// first update the rememberMe incase it was selected
if (value.rememberMe) {
localStorage.setItem("rememberMe", value.rememberMe.toString());
localStorage.setItem("username", value.username);
} else {
localStorage.removeItem("rememberMe");
localStorage.removeItem("username");
}
try {
const response = await fetch("/api/auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
body: JSON.stringify({
username: value.username,
password: value.password,
}),
});
const onSubmitLogin = async (value: z.infer<typeof FormSchema>) => {
// Do something with form data
const data = await response.json();
// first update the rememberMe incase it was selected
if (value.rememberMe) {
localStorage.setItem("rememberMe", value.rememberMe.toString());
localStorage.setItem("username", value.username);
} else {
localStorage.removeItem("rememberMe");
localStorage.removeItem("username");
}
// Store token in localStorage
// localStorage.setItem("auth_token", data.data.token);
if (data.success) {
const prod = btoa(`${value.username.toLowerCase()}:${value.password}`);
const prodUser = { ...data.user, prod: prod };
try {
const response = await fetch("/api/auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({username: value.username, password: value.password}),
});
setSession(prodUser, data.token);
toast.success(`You are logged in as ${data.user.username}`);
router.navigate({ to: "/" });
}
const data = await response.json();
if (!data.success) {
toast.error(`${data.message}`);
}
// Store token in localStorage
// localStorage.setItem("auth_token", data.data.token);
//console.log(data);
} catch (err) {
toast.error("Invalid credentials");
}
};
setSession(data.user, data.token);
toast.success(`You are logged in as ${data.user.username}`);
router.navigate({to: "/"});
} catch (err) {
toast.error("Invalid credentials");
}
};
return (
<div className="ml-[25%]">
<LstCard className="p-3 w-96">
<CardHeader>
<div>
<p className="text-2xl">Login to LST</p>
</div>
</CardHeader>
<hr className="rounded"></hr>
<form onSubmit={handleSubmit(onSubmitLogin)}>
<div>
<Label htmlFor="username" className="m-1">
Username
</Label>
<Input
placeholder="smith001"
{...register("username")}
className={errors.username ? "border-red-500" : ""}
aria-invalid={!!errors.username}
/>
{errors.username && (
<p className="text-red-500 text-sm mt-1">
{errors.username.message}
</p>
)}
</div>
<div>
<>
<Label htmlFor={"password"} className="m-1">
Password
</Label>
<Input
type="password"
{...register("password")}
className={errors.password ? "border-red-500" : ""}
aria-invalid={!!errors.password}
/>
{errors.password && (
<p className="text-red-500 text-sm mt-1">
{errors.password.message}
</p>
)}
</>
</div>
<div className="flex justify-between pt-2">
<div className="flex">
<Controller
render={({ field }) => (
<>
<Checkbox
id="remember"
checked={field.value}
onCheckedChange={field.onChange}
/>
<label
htmlFor="remember"
className="pl-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
remember me
</label>
</>
)}
control={control}
name="rememberMe"
defaultValue={rememeberMe}
/>
</div>
return (
<div className="ml-[25%]">
<LstCard className="p-3 w-96">
<CardHeader>
<div>
<p className="text-2xl">Login to LST</p>
</div>
</CardHeader>
<hr className="rounded"></hr>
<form onSubmit={handleSubmit(onSubmitLogin)}>
<div>
<Label htmlFor="username" className="m-1">
Username
</Label>
<Input
placeholder="smith001"
{...register("username")}
className={errors.username ? "border-red-500" : ""}
aria-invalid={!!errors.username}
/>
{errors.username && <p className="text-red-500 text-sm mt-1">{errors.username.message}</p>}
</div>
<div>
<>
<Label htmlFor={"password"} className="m-1">
Password
</Label>
<Input
type="password"
{...register("password")}
className={errors.password ? "border-red-500" : ""}
aria-invalid={!!errors.password}
/>
{errors.password && <p className="text-red-500 text-sm mt-1">{errors.password.message}</p>}
</>
</div>
<div className="flex justify-between pt-2">
<div className="flex">
<Controller
render={({field}) => (
<>
<Checkbox
id="remember"
checked={field.value}
onCheckedChange={field.onChange}
/>
<label
htmlFor="remember"
className="pl-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
remember me
</label>
</>
)}
control={control}
name="rememberMe"
defaultValue={rememeberMe}
/>
</div>
<div className="flex justify-end">
<Button type="submit">Submit</Button>
</div>
</div>
</form>
</LstCard>
</div>
);
<div className="flex justify-end">
<Button type="submit">Submit</Button>
</div>
</div>
</form>
</LstCard>
</div>
);
};
export default LoginForm;

View File

@@ -15,7 +15,7 @@ import {Collapsible, CollapsibleContent, CollapsibleTrigger} from "../../ui/coll
const items = [
{
title: "Servers",
url: "#",
url: "/servers",
icon: Server,
isActive: false,
},
@@ -31,13 +31,19 @@ const data = {
title: "Settings",
url: "/settings",
icon: Settings,
isActive: true,
},
{
title: "Modules",
url: "/modules",
icon: Settings,
isActive: false,
},
{
title: "Swagger",
url: "#",
icon: Webhook,
isActive: false,
isActive: true,
},
{
title: "Logs",
@@ -91,12 +97,17 @@ export function AdminSideBar() {
<SidebarMenuSub>
{item.items.map((item) => (
<SidebarMenuSubItem key={item.title}>
<SidebarMenuSubButton asChild isActive={item.isActive}>
<a href={item.url} target={item.newWindow ? "_blank" : "_self"}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuSubButton>
{item.isActive && (
<SidebarMenuSubButton asChild>
<a
href={item.url}
target={item.newWindow ? "_blank" : "_self"}
>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuSubButton>
)}
</SidebarMenuSubItem>
))}
</SidebarMenuSub>

View File

@@ -51,6 +51,14 @@ const items = [
module: "logistics",
active: true,
},
{
title: "Ocme Cyclecount",
url: "/cyclecount",
icon: Package,
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
module: "logistics",
active: true,
},
];
export function LogisticsSideBar({user, moduleID}: {user: User | null; moduleID: string}) {

View File

@@ -15,7 +15,7 @@ const items = [
title: "One Click Print",
url: "/ocp",
icon: Printer,
role: ["systemAdmin"],
role: ["viewer"],
module: "ocp",
active: true,
},

View File

@@ -3,6 +3,7 @@ import {Button} from "@/components/ui/button";
import {CardHeader} from "@/components/ui/card";
import {Input} from "@/components/ui/input";
import {Label} from "@/components/ui/label";
import {useSessionStore} from "@/lib/store/sessionStore";
import axios from "axios";
import {useState} from "react";
@@ -12,27 +13,28 @@ import {toast} from "sonner";
export default function ConsumeMaterial() {
const {register: register1, handleSubmit: handleSubmit1, reset} = useForm();
const [submitting, setSubmitting] = useState(false);
const token = localStorage.getItem("auth_token");
const {token} = useSessionStore();
const handleConsume = async (data: any) => {
setSubmitting(!submitting);
setSubmitting(true);
try {
const result = await axios.post(`/api/logistics/consume`, data, {
headers: {Authorization: `Bearer ${token}`},
});
if (result.data.success) {
toast.success(result.data.message);
setSubmitting(!submitting);
setSubmitting(false);
reset();
}
if (!result.data.success) {
//console.log(result.data);
setSubmitting(!submitting);
setSubmitting(false);
toast.error(result.data.message);
}
} catch (error: any) {
//console.log(error);
setSubmitting(!submitting);
setSubmitting(false);
if (error.status === 401) {
toast.error("Unauthorized to do this task.");
} else {

View File

@@ -0,0 +1,71 @@
/**
* we will do a very sloppy way for this just keep fetching the logs we spent to much time on this :()
*/
// //import {useEffect, useState} from "react";
// import {LstCard} from "../extendedUI/LstCard";
// import {CardContent, CardHeader} from "../ui/card";
// import {Skeleton} from "../ui/skeleton";
// import {Button} from "../ui/button";
// import {toast} from "sonner";
// import {useEffect} from "react";
// export default function CycleCountLog() {
// //const [logs, setLogs] = useState([]);
// //const [streaming, setStreaming] = useState(false); // Track if streaming is active
// useEffect(() => {
// // Start streaming when the button is clicked
// let es;
// const url = `http://localhost:4000/api/logger/logs/stream?service=ocme-count&level=info`;
// es = new EventSource(url);
// es.onopen = () => console.log(">>> Connection opened!");
// es.onerror = (e) => console.log("ERROR!", e);
// es.onmessage = (e) => {
// const data = JSON.parse(e.data);
// console.log(e);
// console.log(data);
// switch (data.type) {
// case "time-update":
// console.log(data);
// break;
// case "error":
// console.log(data);
// break;
// case "done":
// console.log(data);
// es.close(); // Close the connection when done
// break;
// default:
// break;
// }
// };
// return () => es.close();
// }, []);
// // const handleStartStreaming = () => {
// // setStreaming(true); // Start streaming when button is clicked
// // };
// return (
// <LstCard className="w-48">
// <CardHeader className="flex justify-center">
// <span>Cycle Count logs</span>
// </CardHeader>
// <CardContent>
// {Array(10)
// .fill(0)
// .map((_, i) => (
// <div key={i}>
// <Skeleton className="m-2 h-4" />
// </div>
// ))}
// </CardContent>
// <Button onClick={() => toast.success("SOmething")}>Start Stream</Button>
// </LstCard>
// );
// }

View File

@@ -0,0 +1,185 @@
import {toast} from "sonner";
import {LstCard} from "../extendedUI/LstCard";
import {Button} from "../ui/button";
import {Input} from "../ui/input";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "../ui/table";
import {Skeleton} from "../ui/skeleton";
//import CycleCountLog from "./CycleCountLog";
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "../ui/select";
import {Controller, useForm} from "react-hook-form";
import axios from "axios";
import {useState} from "react";
export default function OcmeCycleCount() {
const token = localStorage.getItem("auth_token");
const [data, setData] = useState([]);
const [counting, setCounting] = useState(false);
const {
register,
handleSubmit,
//watch,
formState: {errors},
reset,
control,
} = useForm();
const onSubmit = async (data: any) => {
setData([]);
setCounting(true);
toast.success(`Cycle count started`);
try {
const res = await axios.post("/ocme/api/v1/cyclecount", data, {
headers: {Authorization: `Bearer ${token}`},
});
toast.success(res.data.message);
setData(res.data.data);
setCounting(false);
reset();
} catch (error) {
toast.error("There was an error cycle counting");
setCounting(false);
reset();
}
};
return (
<div className="flex flex-row w-screen">
<div className="m-2 w-5/6">
<LstCard>
<p className="ml-2">Please enter the name or laneID you want to cycle count.</p>
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex justify-between">
<div className="m-2 flex flex-row">
<Input
placeholder="enter lane: L064"
className={errors.lane ? "border-red-500" : ""}
aria-invalid={!!errors.lane}
{...register("lane", {
required: true,
minLength: {
value: 3,
message: "The lane is too short!",
},
})}
/>
<div className="ml-2">
<Controller
control={control}
name="laneType"
defaultValue={""}
render={({
field: {onChange},
fieldState: {},
//formState,
}) => (
<Select onValueChange={onChange}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select name or id" />
</SelectTrigger>
<SelectContent>
<SelectItem value="name">Name</SelectItem>
<SelectItem value="laneId">Lane ID</SelectItem>
</SelectContent>
</Select>
)}
/>
</div>
</div>
<Button className="m-2" type="submit" disabled={counting}>
{counting ? <span>Counting...</span> : <span>CycleCount</span>}
</Button>
</div>
</form>
</div>
<div>
<Table>
<TableHeader>
<TableRow>
<TableHead>LaneID</TableHead>
<TableHead>Lane</TableHead>
<TableHead>AV</TableHead>
<TableHead>Description</TableHead>
<TableHead>Running Number</TableHead>
<TableHead>In Ocme</TableHead>
<TableHead>In Stock</TableHead>
<TableHead>Result</TableHead>
</TableRow>
</TableHeader>
{data.length === 0 ? (
<TableBody>
{Array(10)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
) : (
<>
{data.map((i: any) => {
let classname = ``;
if (i.info === "Quality Check Required") {
classname = `bg-red-500`;
}
if (i.info === "Sent to Inv") {
classname = `bg-amber-700`;
}
return (
<TableRow key={i.runningNumber}>
<TableCell className={`font-medium ${classname}`}>
{i.alpla_laneID}
</TableCell>
<TableCell className={`font-medium ${classname}`}>
{i.alpla_laneDescription}
</TableCell>
<TableCell className={`font-medium ${classname}`}>
{i.Article}
</TableCell>
<TableCell className={`font-medium ${classname}`}>
{i.alpla_laneDescription}
</TableCell>
<TableCell className={`font-medium ${classname}`}>
{i.runningNumber}
</TableCell>
<TableCell className={`font-medium ${classname}`}>{i.ocme}</TableCell>
<TableCell className={`font-medium ${classname}`}>{i.stock}</TableCell>
<TableCell className={`font-medium ${classname}`}>{i.info}</TableCell>
</TableRow>
);
})}
</>
)}
</Table>
</div>
</LstCard>
</div>
{/* <div className="m-2">
<CycleCountLog />
</div> */}
</div>
);
}

View File

@@ -1,5 +1,125 @@
import {LstCard} from "@/components/extendedUI/LstCard";
import {Skeleton} from "@/components/ui/skeleton";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
// import {useSessionStore} from "@/lib/store/sessionStore";
// import {useSettingStore} from "@/lib/store/useSettings";
import {useQuery} from "@tanstack/react-query";
import {getlabels} from "@/utils/querys/production/labels";
import {format} from "date-fns";
const labelLogs = [
{key: "line", label: "Line"},
{key: "printerName", label: "Printer"},
{key: "runningNr", label: "Running #"},
{key: "upd_date", label: "Label date"},
{key: "status", label: "Label Status"},
//{key: "reprint", label: "Reprint"}, // removing the reprint button for now until repritning is working as intended
];
export default function LabelLog() {
return <LstCard className="m-2 p-2"> label logs here</LstCard>;
const {data, isError, isLoading} = useQuery(getlabels("4"));
//const {user} = useSessionStore();
//const {settings} = useSettingStore();
//const server = settings.filter((n) => n.name === "server")[0]?.value || "";
//const roles = ["admin", "manager", "operator"];
if (isError) {
return (
<div className="m-2 p-2 min-h-2/5">
<LstCard>
<p className="text-center">Labels for the last 2 hours</p>
<Table>
<TableHeader>
<TableRow>
{labelLogs.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{Array(7)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</LstCard>
</div>
);
}
return (
<LstCard className="m-2 p-2 min-h-2/5">
<p className="text-center">Labels for the last 2 hours</p>
<Table>
<TableHeader>
<TableRow>
{labelLogs.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
{isLoading ? (
<>
<TableBody>
{Array(7)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</>
) : (
<TableBody>
{data?.map((label: any) => (
<TableRow key={label.runningNr}>
<TableCell className="font-medium">{label.line}</TableCell>
<TableCell className="font-medium">{label.printerName}</TableCell>
<TableCell className="font-medium">{label.runningNr}</TableCell>
<TableCell className="font-medium">
{format(label.upd_date, "M/d/yyyy hh:mm")}
</TableCell>
<TableCell className="font-medium">{label.status}</TableCell>
</TableRow>
))}
</TableBody>
)}
</Table>
</LstCard>
);
}

View File

@@ -1,9 +1,222 @@
import {LstCard} from "@/components/extendedUI/LstCard";
import {Skeleton} from "@/components/ui/skeleton";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {useSessionStore} from "@/lib/store/sessionStore";
import {useSettingStore} from "@/lib/store/useSettings";
import {LotType} from "@/types/lots";
import {getlots} from "@/utils/querys/production/lots";
import {useQuery} from "@tanstack/react-query";
import ManualPrint from "./ManualPrinting/ManualPrint";
import ManualPrintForm from "./ManualPrinting/ManualPrintForm";
let lotColumns = [
{
key: "MachineDescription",
label: "Machine",
},
{
key: "AV",
label: "AV",
},
{
key: "Alias",
label: "AvDescription",
},
{
key: "LOT",
label: "LotNumber",
},
{
key: "ProlinkLot",
label: "ProlinkLot",
},
{
key: "PlannedQTY",
label: "PlannedQTY",
},
{
key: "Produced",
label: "Produced",
},
{
key: "Remaining",
label: "Remaining",
},
{
key: "overPrinting",
label: "Overprinting",
},
// {
// key: "lastProlinkUpdate",
// label: "Last ProlinkCheck",
// },
// {
// key: "printLabel",
// label: "Print Label",
// },
];
export default function Lots() {
const {data, isError, isLoading} = useQuery(getlots());
const {user} = useSessionStore();
const {settings} = useSettingStore();
const server = settings.filter((n) => n.name === "server")[0]?.value || "";
console.log(server);
const roles = ["admin", "manager", "operator"];
if (user && roles.includes(user.role)) {
//width = 1280;
const checkCol = lotColumns.some((l) => l.key === "printLabel");
if (!checkCol) {
lotColumns = [
...lotColumns,
{
key: "printLabel",
label: "Print Label",
},
];
}
}
if (isError) {
return (
<div className="m-2 p-2 min-h-2/5">
<LstCard>
<p className="text-center">Current Assigned lots</p>
<Table>
<TableHeader>
<TableRow>
{lotColumns.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{Array(10)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</LstCard>
</div>
);
}
return (
<LstCard className="m-2 p-2 min-h-2/5">
<h1>Lots</h1>
<p className="text-center">Current Assigned lots</p>
<Table>
<TableHeader>
<TableRow>
{lotColumns.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
{isLoading ? (
<>
<TableBody>
{Array(10)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</>
) : (
<TableBody>
{data?.map((lot: LotType) => (
<TableRow key={lot.LabelOnlineID}>
<TableCell className="font-medium">{lot.MachineLocation}</TableCell>
<TableCell className="font-medium">{lot.AV}</TableCell>
<TableCell className="font-medium">{lot.Alias}</TableCell>
<TableCell className="font-medium">{lot.LOT}</TableCell>
<TableCell className="font-medium">{lot.ProlinkLot}</TableCell>
<TableCell className="font-medium">{lot.PlannedQTY}</TableCell>
<TableCell className="font-medium">{lot.Produced}</TableCell>
<TableCell className="font-medium">{lot.Remaining}</TableCell>
<TableCell className="font-medium">{lot.overPrinting}</TableCell>
{user && roles.includes(user.role) && (
<>
{server === "usday1vms006" || server === "localhost" ? (
<>
<TableCell className="flex justify-center">
<ManualPrintForm lot={lot} />
</TableCell>
</>
) : (
<TableCell className="flex justify-center">
<ManualPrint lot={lot} />
</TableCell>
)}
</>
)}
</TableRow>
))}
</TableBody>
)}
</Table>
</LstCard>
);
}

View File

@@ -0,0 +1,32 @@
import {Button} from "@/components/ui/button";
import {useSessionStore} from "@/lib/store/sessionStore";
//import {useSettingStore} from "@/lib/store/useSettings";
import {LotType} from "@/types/lots";
import {Tag} from "lucide-react";
import {toast} from "sonner";
import {manualPrintLabels} from "./ManualPrintLabel";
export default function ManualPrint({lot}: {lot: LotType}) {
const {user} = useSessionStore();
//const {settings} = useSettingStore();
//const server = settings.filter((n) => n.name === "server")[0]?.value;
//const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
//const serverUrl = `http://${server}:${serverPort}`;
const handlePrintLabel = async (lot: LotType) => {
//console.log(lot);
const labels: any = await manualPrintLabels(lot, user);
if (labels.success) {
toast.success(labels.message);
} else {
toast.error(labels.message);
}
};
return (
<Button variant="outline" size="icon" onClick={() => handlePrintLabel(lot)}>
<Tag className="h-[16px] w-[16px]" />
</Button>
);
}

View File

@@ -0,0 +1,217 @@
import {Button} from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {Input} from "@/components/ui/input";
import {Label} from "@/components/ui/label";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {Textarea} from "@/components/ui/textarea";
import {useSessionStore} from "@/lib/store/sessionStore";
import {useSettingStore} from "@/lib/store/useSettings";
import {LotType} from "@/types/lots";
import axios from "axios";
import {Tag} from "lucide-react";
import {useState} from "react";
import {Controller, useForm} from "react-hook-form";
import {toast} from "sonner";
import {manualPrintLabels} from "./ManualPrintLabel";
const printReason = [
{key: "printerIssue", label: "Printer Related"},
{key: "strapper", label: "Strapper Error"},
{key: "manualCheck", label: "20th pallet check"},
{key: "outOfSync", label: "Labeler Out of Sync"},
];
export default function ManualPrintForm({lot}: {lot: LotType}) {
const {user} = useSessionStore();
const token = localStorage.getItem("auth_token");
const {settings} = useSettingStore();
const [open, setOpen] = useState(false);
const server = settings.filter((n) => n.name === "server")[0]?.value;
// const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
// const serverUrl = `http://${server}:${serverPort}`;
const {
register,
handleSubmit,
//watch,
formState: {errors},
reset,
control,
} = useForm();
const handlePrintLabel = async (lot: LotType) => {
//console.log(lot);
const labels: any = await manualPrintLabels(lot, user);
if (labels.success) {
toast.success(labels.message);
} else {
toast.error(labels.message);
}
};
const handleManualPrintLog = async (logData: any, lot: LotType) => {
// toast.success(`A new label was sent to printer: ${lot.PrinterName} for line ${lot.MachineDescription} `);
const logdataUrl = `/api/ocp/manualLabelLog`;
axios
.post(logdataUrl, logData, {headers: {Authorization: `Bearer ${token}`}})
.then((d) => {
//console.log(d);
toast.success(d.data.message);
handlePrintLabel(lot);
reset();
})
.catch((e) => {
if (e.response.status === 500) {
toast.error(`Internal Server error please try again.`);
return {sucess: false};
}
if (e.response.status === 401) {
//console.log(e.response);
toast.error(`You are not authorized to do this.`);
return {sucess: false};
}
});
};
const onSubmit = (data: any) => {
console.log(data);
handleManualPrintLog(data, lot);
};
return (
<Dialog
open={open}
onOpenChange={(isOpen) => {
if (!open) {
reset();
}
setOpen(isOpen);
// toast.message("Model was something", {
// description: isOpen ? "Modal is open" : "Modal is closed",
// });
}}
>
<DialogTrigger asChild>
<Button variant="outline" size="icon">
<Tag className="h-[16px] w-[16px]" />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit(onSubmit)}>
<p>
To manually print a label you must complete all the required fields below.
<br />
If you clicked this in error just click close
</p>
<hr className="mt-2 mb-2" />
{server == "usday1vms006" ? (
<Controller
control={control}
name="printReason"
defaultValue={""}
render={({
field: {onChange},
fieldState: {},
//formState,
}) => (
<Select onValueChange={onChange}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select Reason" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Print Reasons</SelectLabel>
{printReason.map((printReason: any) => (
<SelectItem value={printReason.key}>{printReason.label}</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
)}
/>
) : (
<div>
<Label htmlFor="printRason" className="m-1">
Why are you manually printing?
</Label>
<Input
type="text"
className={errors.printReason ? "border-red-500" : ""}
aria-invalid={!!errors.printReason}
{...register("printReason", {
required: true,
minLength: {
value: 5,
message: "To short of a reason please try again!",
},
})}
/>
</div>
)}
<div>
<Label htmlFor="line" className="m-1">
"What is the line number you are printing?"
</Label>
<Input
//variant="underlined"
type="number"
className={errors.line ? "border-red-500" : ""}
aria-invalid={!!errors.line}
{...register("line", {required: true})}
/>
</div>
<div>
<Label htmlFor="initials" className="m-1">
Enter intials
</Label>
<Input
//variant="underlined"
//label="Enter intials"
{...register("initials", {required: true})}
/>
</div>
<Textarea
//label="Comments"
placeholder="add more info as needed."
{...register("additionalComments")}
/>
<DialogFooter>
<Button color="danger" variant="default" onClick={() => setOpen(!open)}>
Close
</Button>
<Button color="primary" type="submit">
Print
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,44 @@
import {LotType} from "@/types/lots";
import axios from "axios";
export const manualPrintLabels = async (lot: LotType, user: any) => {
//console.log(lot);
const labelUrl = `/ocp/manualPrintAndFollow`;
try {
const res = await axios.post(
labelUrl,
{line: lot.MachineLocation, printerName: lot.PrinterName},
{headers: {Authorization: `Basic ${user?.prod}`}}
);
if (res.data.success) {
return {
success: true,
message: `A new label was printed for ${lot.MachineDescription} to printer: ${lot.PrinterName}`,
};
} else {
return {
success: true,
message: `Line ${lot.MachineDescription} encountered an error printing labels: ${res.data.message}`,
};
}
} catch (error: any) {
if (error.response.status === 500) {
//toast.error(`Internal Server error please try again.`);
return {
success: false,
message: `Internal Server error please try again.`,
};
}
if (error.response.status === 401) {
//console.log(e.response);
//toast.error(`You are not authorized to do this.`);
return {
success: false,
message: `You are not authorized to do this.`,
};
}
}
};

View File

@@ -1,5 +1,52 @@
import {LstCard} from "@/components/extendedUI/LstCard";
import {Skeleton} from "@/components/ui/skeleton";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
let printerCols = [
{
key: "status",
label: "Status",
},
{
key: "printer",
label: "Printer",
},
{
key: "statusMessage",
label: "Status Message",
},
];
export default function PrinterStatus() {
return <LstCard className="m-2 p-2">Printer Status</LstCard>;
return (
<LstCard className="m-2 p-2">
<p className="text-center">Printer Status</p>
<Table>
<TableHeader>
<TableRow>
{printerCols.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{Array(10)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</LstCard>
);
}

View File

@@ -1,16 +1,19 @@
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
import {useModuleStore} from "../../lib/store/useModuleStore";
import {useEffect} from "react";
import {useSettingStore} from "@/lib/store/useSettings";
//import {useGetUserRoles} from "@/lib/store/useGetRoles";
const queryClient = new QueryClient();
export const SessionProvider = ({children}: {children: React.ReactNode}) => {
const {fetchModules} = useModuleStore();
const {fetchSettings} = useSettingStore();
//const {fetchUserRoles} = useGetUserRoles();
useEffect(() => {
fetchModules();
fetchSettings();
//fetchUserRoles();
}, []);
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;

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

@@ -0,0 +1,183 @@
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Select({
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot="select" {...props} />
}
function SelectGroup({
...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
return <SelectPrimitive.Group data-slot="select-group" {...props} />
}
function SelectValue({
...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
return <SelectPrimitive.Value data-slot="select-value" {...props} />
}
function SelectTrigger({
className,
size = "default",
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default"
}) {
return (
<SelectPrimitive.Trigger
data-slot="select-trigger"
data-size={size}
className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDownIcon className="size-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
)
}
function SelectContent({
className,
children,
position = "popper",
...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
return (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
data-slot="select-content"
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
}
function SelectLabel({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
return (
<SelectPrimitive.Label
data-slot="select-label"
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
{...props}
/>
)
}
function SelectItem({
className,
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
return (
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
)}
{...props}
>
<span className="absolute right-2 flex size-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
)
}
function SelectSeparator({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
return (
<SelectPrimitive.Separator
data-slot="select-separator"
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
{...props}
/>
)
}
function SelectScrollUpButton({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
return (
<SelectPrimitive.ScrollUpButton
data-slot="select-scroll-up-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronUpIcon className="size-4" />
</SelectPrimitive.ScrollUpButton>
)
}
function SelectScrollDownButton({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
return (
<SelectPrimitive.ScrollDownButton
data-slot="select-scroll-down-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronDownIcon className="size-4" />
</SelectPrimitive.ScrollDownButton>
)
}
export {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
}

View File

@@ -0,0 +1,18 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
<textarea
data-slot="textarea"
className={cn(
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
{...props}
/>
)
}
export { Textarea }

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

@@ -17,6 +17,16 @@ const fetchSession = async () => {
});
// console.log(res);
if (!res.ok) {
localStorage.removeItem("auth_token");
// remove these for a while if no session just until fully to 2.0 and clearly no one has ran lstv1 in a long time
localStorage.removeItem("ally-supports-cache");
localStorage.removeItem("auth-storage");
localStorage.removeItem("nextauth.message");
localStorage.removeItem("prod");
localStorage.removeItem("cards");
localStorage.removeItem("rememberMe");
localStorage.removeItem("username");
throw new Error("Session not found");
}

View File

@@ -1,4 +1,5 @@
import {User} from "@/types/users";
import axios from "axios";
import {create} from "zustand";
export type SessionState = {
@@ -16,9 +17,14 @@ export const useSessionStore = create<SessionState>((set) => {
user: null, // User is NOT stored in localStorage
token: storedToken || null,
setSession: (user, token) => {
setSession: async (user: any, token) => {
if (token) {
localStorage.setItem("auth_token", token);
const response = await axios.get("/api/auth/getuseraccess", {
headers: {Authorization: `Bearer ${token}`},
});
const data = response.data; //await response.json();
user = {...user, roles: data.data};
} else {
localStorage.removeItem("auth_token");
}

View File

@@ -1,6 +1,7 @@
import {create} from "zustand";
import {useSessionStore} from "./sessionStore";
import {Modules} from "@/types/modules";
import axios from "axios";
interface SettingState {
userRoles: Modules[];
@@ -19,15 +20,8 @@ export const useGetUserRoles = create<SettingState>()((set) => ({
try {
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
const {token} = useSessionStore();
const response = await fetch(`/api/auth/getuseraccess`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authentication: `Beaer ${token}`,
// You can add other headers here if necessary
},
});
const data: FetchModulesResponse = await response.json();
const response = await axios.get("/api/auth/getuseraccess", {headers: {Authorization: `Bearer ${token}`}});
const data: FetchModulesResponse = response.data; //await response.json();
//console.log(data);
set({userRoles: data.data});
} catch (error) {

View File

@@ -1,4 +1,5 @@
import {Modules} from "@/types/modules";
import axios from "axios";
import {create} from "zustand";
interface SettingState {
@@ -17,18 +18,13 @@ export const useModuleStore = create<SettingState>()((set) => ({
fetchModules: async () => {
try {
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
const response = await fetch(`/api/server/modules`, {
method: "GET",
headers: {
"Content-Type": "application/json",
// You can add other headers here if necessary
},
});
const data: FetchModulesResponse = await response.json();
const response = await axios.get(`/api/server/modules`, {});
const data: FetchModulesResponse = response.data; //await response.json();
//console.log(data);
set({modules: data.data});
} catch (error) {
console.error("Failed to fetch settings:", error);
set({modules: []});
}
},
}));

View File

@@ -0,0 +1,29 @@
import axios from "axios";
import {create} from "zustand";
interface SettingState {
settings: any[];
fetchSettings: () => Promise<void>;
setSettings: (settings: any[]) => void;
}
interface FetchModulesResponse {
data: any[];
}
export const useSettingStore = create<SettingState>()((set) => ({
settings: [],
setSettings: (settings) => set({settings}),
fetchSettings: async () => {
try {
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
const response = await axios.get(`/api/server/settings`, {});
const data: FetchModulesResponse = response.data; //await response.json();
//console.log(data);
set({settings: data.data});
} catch (error) {
console.error("Failed to fetch settings:", error);
set({settings: []});
}
},
}));

View File

@@ -18,11 +18,12 @@ import { Route as AuthImport } from './routes/_auth'
import { Route as AdminImport } from './routes/_admin'
import { Route as IndexImport } from './routes/index'
import { Route as OcpIndexImport } from './routes/ocp/index'
import { Route as OcpLotsImport } from './routes/ocp/lots'
import { Route as EomEomImport } from './routes/_eom/eom'
import { Route as AuthProfileImport } from './routes/_auth/profile'
import { Route as AdminSettingsImport } from './routes/_admin/settings'
import { Route as AdminServersImport } from './routes/_admin/servers'
import { Route as AdminModulesImport } from './routes/_admin/modules'
import { Route as ocmeCyclecountIndexImport } from './routes/(ocme)/cyclecount/index'
import { Route as logisticsMaterialHelperIndexImport } from './routes/(logistics)/materialHelper/index'
import { Route as EomArticleAvImport } from './routes/_eom/article/$av'
import { Route as logisticsMaterialHelperSiloLinkIndexImport } from './routes/(logistics)/materialHelper/siloLink/index'
@@ -69,12 +70,6 @@ const OcpIndexRoute = OcpIndexImport.update({
getParentRoute: () => rootRoute,
} as any)
const OcpLotsRoute = OcpLotsImport.update({
id: '/ocp/lots',
path: '/ocp/lots',
getParentRoute: () => rootRoute,
} as any)
const EomEomRoute = EomEomImport.update({
id: '/eom',
path: '/eom',
@@ -93,12 +88,24 @@ const AdminSettingsRoute = AdminSettingsImport.update({
getParentRoute: () => AdminRoute,
} as any)
const AdminServersRoute = AdminServersImport.update({
id: '/servers',
path: '/servers',
getParentRoute: () => AdminRoute,
} as any)
const AdminModulesRoute = AdminModulesImport.update({
id: '/modules',
path: '/modules',
getParentRoute: () => AdminRoute,
} as any)
const ocmeCyclecountIndexRoute = ocmeCyclecountIndexImport.update({
id: '/(ocme)/cyclecount/',
path: '/cyclecount/',
getParentRoute: () => rootRoute,
} as any)
const logisticsMaterialHelperIndexRoute =
logisticsMaterialHelperIndexImport.update({
id: '/(logistics)/materialHelper/',
@@ -179,6 +186,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AdminModulesImport
parentRoute: typeof AdminImport
}
'/_admin/servers': {
id: '/_admin/servers'
path: '/servers'
fullPath: '/servers'
preLoaderRoute: typeof AdminServersImport
parentRoute: typeof AdminImport
}
'/_admin/settings': {
id: '/_admin/settings'
path: '/settings'
@@ -200,13 +214,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof EomEomImport
parentRoute: typeof EomImport
}
'/ocp/lots': {
id: '/ocp/lots'
path: '/ocp/lots'
fullPath: '/ocp/lots'
preLoaderRoute: typeof OcpLotsImport
parentRoute: typeof rootRoute
}
'/ocp/': {
id: '/ocp/'
path: '/ocp'
@@ -228,6 +235,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof logisticsMaterialHelperIndexImport
parentRoute: typeof rootRoute
}
'/(ocme)/cyclecount/': {
id: '/(ocme)/cyclecount/'
path: '/cyclecount'
fullPath: '/cyclecount'
preLoaderRoute: typeof ocmeCyclecountIndexImport
parentRoute: typeof rootRoute
}
'/(logistics)/materialHelper/consumption/': {
id: '/(logistics)/materialHelper/consumption/'
path: '/materialHelper/consumption'
@@ -249,11 +263,13 @@ declare module '@tanstack/react-router' {
interface AdminRouteChildren {
AdminModulesRoute: typeof AdminModulesRoute
AdminServersRoute: typeof AdminServersRoute
AdminSettingsRoute: typeof AdminSettingsRoute
}
const AdminRouteChildren: AdminRouteChildren = {
AdminModulesRoute: AdminModulesRoute,
AdminServersRoute: AdminServersRoute,
AdminSettingsRoute: AdminSettingsRoute,
}
@@ -287,13 +303,14 @@ export interface FileRoutesByFullPath {
'/about': typeof AboutRoute
'/login': typeof LoginRoute
'/modules': typeof AdminModulesRoute
'/servers': typeof AdminServersRoute
'/settings': typeof AdminSettingsRoute
'/profile': typeof AuthProfileRoute
'/eom': typeof EomEomRoute
'/ocp/lots': typeof OcpLotsRoute
'/ocp': typeof OcpIndexRoute
'/article/$av': typeof EomArticleAvRoute
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
'/cyclecount': typeof ocmeCyclecountIndexRoute
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -304,13 +321,14 @@ export interface FileRoutesByTo {
'/about': typeof AboutRoute
'/login': typeof LoginRoute
'/modules': typeof AdminModulesRoute
'/servers': typeof AdminServersRoute
'/settings': typeof AdminSettingsRoute
'/profile': typeof AuthProfileRoute
'/eom': typeof EomEomRoute
'/ocp/lots': typeof OcpLotsRoute
'/ocp': typeof OcpIndexRoute
'/article/$av': typeof EomArticleAvRoute
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
'/cyclecount': typeof ocmeCyclecountIndexRoute
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -324,13 +342,14 @@ export interface FileRoutesById {
'/about': typeof AboutRoute
'/login': typeof LoginRoute
'/_admin/modules': typeof AdminModulesRoute
'/_admin/servers': typeof AdminServersRoute
'/_admin/settings': typeof AdminSettingsRoute
'/_auth/profile': typeof AuthProfileRoute
'/_eom/eom': typeof EomEomRoute
'/ocp/lots': typeof OcpLotsRoute
'/ocp/': typeof OcpIndexRoute
'/_eom/article/$av': typeof EomArticleAvRoute
'/(logistics)/materialHelper/': typeof logisticsMaterialHelperIndexRoute
'/(ocme)/cyclecount/': typeof ocmeCyclecountIndexRoute
'/(logistics)/materialHelper/consumption/': typeof logisticsMaterialHelperConsumptionIndexRoute
'/(logistics)/materialHelper/siloLink/': typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -343,13 +362,14 @@ export interface FileRouteTypes {
| '/about'
| '/login'
| '/modules'
| '/servers'
| '/settings'
| '/profile'
| '/eom'
| '/ocp/lots'
| '/ocp'
| '/article/$av'
| '/materialHelper'
| '/cyclecount'
| '/materialHelper/consumption'
| '/materialHelper/siloLink'
fileRoutesByTo: FileRoutesByTo
@@ -359,13 +379,14 @@ export interface FileRouteTypes {
| '/about'
| '/login'
| '/modules'
| '/servers'
| '/settings'
| '/profile'
| '/eom'
| '/ocp/lots'
| '/ocp'
| '/article/$av'
| '/materialHelper'
| '/cyclecount'
| '/materialHelper/consumption'
| '/materialHelper/siloLink'
id:
@@ -377,13 +398,14 @@ export interface FileRouteTypes {
| '/about'
| '/login'
| '/_admin/modules'
| '/_admin/servers'
| '/_admin/settings'
| '/_auth/profile'
| '/_eom/eom'
| '/ocp/lots'
| '/ocp/'
| '/_eom/article/$av'
| '/(logistics)/materialHelper/'
| '/(ocme)/cyclecount/'
| '/(logistics)/materialHelper/consumption/'
| '/(logistics)/materialHelper/siloLink/'
fileRoutesById: FileRoutesById
@@ -396,9 +418,9 @@ export interface RootRouteChildren {
EomRoute: typeof EomRouteWithChildren
AboutRoute: typeof AboutRoute
LoginRoute: typeof LoginRoute
OcpLotsRoute: typeof OcpLotsRoute
OcpIndexRoute: typeof OcpIndexRoute
logisticsMaterialHelperIndexRoute: typeof logisticsMaterialHelperIndexRoute
ocmeCyclecountIndexRoute: typeof ocmeCyclecountIndexRoute
logisticsMaterialHelperConsumptionIndexRoute: typeof logisticsMaterialHelperConsumptionIndexRoute
logisticsMaterialHelperSiloLinkIndexRoute: typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -410,9 +432,9 @@ const rootRouteChildren: RootRouteChildren = {
EomRoute: EomRouteWithChildren,
AboutRoute: AboutRoute,
LoginRoute: LoginRoute,
OcpLotsRoute: OcpLotsRoute,
OcpIndexRoute: OcpIndexRoute,
logisticsMaterialHelperIndexRoute: logisticsMaterialHelperIndexRoute,
ocmeCyclecountIndexRoute: ocmeCyclecountIndexRoute,
logisticsMaterialHelperConsumptionIndexRoute:
logisticsMaterialHelperConsumptionIndexRoute,
logisticsMaterialHelperSiloLinkIndexRoute:
@@ -435,9 +457,9 @@ export const routeTree = rootRoute
"/_eom",
"/about",
"/login",
"/ocp/lots",
"/ocp/",
"/(logistics)/materialHelper/",
"/(ocme)/cyclecount/",
"/(logistics)/materialHelper/consumption/",
"/(logistics)/materialHelper/siloLink/"
]
@@ -449,6 +471,7 @@ export const routeTree = rootRoute
"filePath": "_admin.tsx",
"children": [
"/_admin/modules",
"/_admin/servers",
"/_admin/settings"
]
},
@@ -475,6 +498,10 @@ export const routeTree = rootRoute
"filePath": "_admin/modules.tsx",
"parent": "/_admin"
},
"/_admin/servers": {
"filePath": "_admin/servers.tsx",
"parent": "/_admin"
},
"/_admin/settings": {
"filePath": "_admin/settings.tsx",
"parent": "/_admin"
@@ -487,9 +514,6 @@ export const routeTree = rootRoute
"filePath": "_eom/eom.tsx",
"parent": "/_eom"
},
"/ocp/lots": {
"filePath": "ocp/lots.tsx"
},
"/ocp/": {
"filePath": "ocp/index.tsx"
},
@@ -500,6 +524,9 @@ export const routeTree = rootRoute
"/(logistics)/materialHelper/": {
"filePath": "(logistics)/materialHelper/index.tsx"
},
"/(ocme)/cyclecount/": {
"filePath": "(ocme)/cyclecount/index.tsx"
},
"/(logistics)/materialHelper/consumption/": {
"filePath": "(logistics)/materialHelper/consumption/index.tsx"
},

View File

@@ -0,0 +1,14 @@
import OcmeCycleCount from "@/components/ocme/ocmeCycleCount";
import {createFileRoute} from "@tanstack/react-router";
export const Route = createFileRoute("/(ocme)/cyclecount/")({
component: RouteComponent,
});
function RouteComponent() {
return (
<div className="m-2">
<OcmeCycleCount />
</div>
);
}

View File

@@ -1,91 +1,94 @@
import {createRootRoute, Link, Outlet} from "@tanstack/react-router";
import {TanStackRouterDevtools} from "@tanstack/router-devtools";
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
//import {TanStackRouterDevtools} from "@tanstack/router-devtools";
import Cookies from "js-cookie";
import {SidebarProvider} from "../components/ui/sidebar";
import {ThemeProvider} from "../components/layout/theme-provider";
import {ModeToggle} from "../components/layout/mode-toggle";
import {AppSidebar} from "../components/layout/lst-sidebar";
import {Avatar, AvatarFallback, AvatarImage} from "../components/ui/avatar";
import { SidebarProvider } from "../components/ui/sidebar";
import { ThemeProvider } from "../components/layout/theme-provider";
import { ModeToggle } from "../components/layout/mode-toggle";
import { AppSidebar } from "../components/layout/lst-sidebar";
import { Avatar, AvatarFallback, AvatarImage } from "../components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../components/ui/dropdown-menu";
import {SessionProvider} from "../components/providers/Providers";
import {Toaster} from "sonner";
import {Button} from "../components/ui/button";
import { SessionProvider } from "../components/providers/Providers";
import { Toaster } from "sonner";
//import { Button } from "../components/ui/button";
import {useSessionStore} from "../lib/store/sessionStore";
import {useSession} from "@/hooks/useSession";
import {useLogout} from "@/hooks/useLogout";
import { useSessionStore } from "../lib/store/sessionStore";
import { useSession } from "@/hooks/useSession";
import { useLogout } from "@/hooks/useLogout";
// same as the layout
export const Route = createRootRoute({
component: () => {
const sidebarState = Cookies.get("sidebar_state") === "true";
const {session} = useSession();
const {user} = useSessionStore();
const logout = useLogout();
component: () => {
const sidebarState = Cookies.get("sidebar_state") === "true";
const { session } = useSession();
const { user } = useSessionStore();
const logout = useLogout();
return (
<>
<SessionProvider>
<ThemeProvider>
<nav className="flex justify-end">
<div className="m-2 flex flex-row">
<div className="m-auto pr-2">
<p>Add Card</p>
</div>
<div className="m-1">
<ModeToggle />
</div>
{session ? (
<div className="m-1">
<DropdownMenu>
<DropdownMenuTrigger>
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Hello {user?.username}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Profile</DropdownMenuItem>
return (
<>
<SessionProvider>
<ThemeProvider>
<nav className="flex justify-end">
<div className="m-2 flex flex-row">
<div className="m-auto pr-2">
<p>Add Card</p>
</div>
<div className="m-1">
<ModeToggle />
</div>
{session ? (
<div className="m-1">
<DropdownMenu>
<DropdownMenuTrigger>
<Avatar>
<AvatarImage
src="https://github.com/shadcn.png"
alt="@shadcn"
/>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>
Hello {user?.username}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{/* <DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem>
<DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuItem>Subscription</DropdownMenuItem>
<hr className="solid"></hr>
<DropdownMenuItem>
<div className="m-auto mt-3">
<Button onClick={() => logout()} variant="ghost">
Logout
</Button>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
) : (
<>
<Link to="/login">Login</Link>
</>
)}
</div>
</nav>
<SidebarProvider defaultOpen={sidebarState}>
<AppSidebar />
<Toaster expand={true} richColors closeButton />
<Outlet />
</SidebarProvider>
</ThemeProvider>
</SessionProvider>
<DropdownMenuItem>Subscription</DropdownMenuItem> */}
<hr className="solid"></hr>
<DropdownMenuItem>
<div className="m-auto">
<button onClick={() => logout()}>Logout</button>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
) : (
<div>
<Link to="/login">Login</Link>
</div>
)}
</div>
</nav>
<SidebarProvider defaultOpen={sidebarState}>
<AppSidebar />
<Toaster expand={true} richColors closeButton />
<Outlet />
</SidebarProvider>
</ThemeProvider>
</SessionProvider>
<TanStackRouterDevtools position="bottom-right" />
</>
);
},
{/* <TanStackRouterDevtools position="bottom-right" /> */}
</>
);
},
});

View File

@@ -0,0 +1,14 @@
import ServerPage from "@/components/admin/servers/ServerPage";
import {createFileRoute} from "@tanstack/react-router";
export const Route = createFileRoute("/_admin/servers")({
component: RouteComponent,
});
function RouteComponent() {
return (
<div>
<ServerPage />
</div>
);
}

View File

@@ -1,9 +0,0 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/ocp/lots')({
component: RouteComponent,
})
function RouteComponent() {
return <div>Hello "/ocp/lots"!</div>
}

View File

@@ -0,0 +1,19 @@
export type LotType = {
AV: number;
Alias: string;
LOT: number;
LabelOnlineID: number;
MachineDescription: string;
MachineID: number;
MachineLocation: number;
PlannedQTY: number;
PrinterName: string;
Produced: number;
ProlinkLot: number;
Remaining: number;
machineID: number;
overPrinting: string;
pallerCopies: number;
palletLabel: string;
printerID: number;
};

View File

@@ -6,4 +6,5 @@ export type User = {
username: string;
roles: Roles[];
role: string;
prod?: string;
};

View File

@@ -0,0 +1,8 @@
export const adminUrlCheck = () => {
const host = window.location.host.split(":")[0];
const okHost = ["localhost", "usmcd1vms036"];
if (okHost.includes(host)) {
return true;
}
return false;
};

View File

@@ -0,0 +1,20 @@
import {queryOptions} from "@tanstack/react-query";
import axios from "axios";
export function getlabels(hours: string) {
return queryOptions({
queryKey: ["labels"],
queryFn: () => fetchSettings(hours),
staleTime: 1000,
//refetchInterval: 2500,
refetchOnWindowFocus: true,
});
}
const fetchSettings = async (hours: string) => {
const {data} = await axios.get(`/api/v1/ocp/labels?hours=${hours}`);
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];
return data.data ?? [];
};

View File

@@ -0,0 +1,21 @@
import {queryOptions} from "@tanstack/react-query";
import axios from "axios";
export function getlots() {
return queryOptions({
queryKey: ["lots"],
queryFn: () => fetchSettings(),
staleTime: 10 * 1000,
//refetchInterval: 10 * 1000,
refetchOnWindowFocus: true,
});
}
const fetchSettings = async () => {
const {data} = await axios.get("/api/v1/ocp/lots");
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];
let lotData = data.data;
return lotData ?? [];
};

View File

@@ -0,0 +1,19 @@
import {queryOptions} from "@tanstack/react-query";
import axios from "axios";
export function getServers(token: string) {
return queryOptions({
queryKey: ["servers"],
queryFn: () => fetchSettings(token),
enabled: !!token,
staleTime: 1000,
refetchInterval: 2500,
refetchOnWindowFocus: true,
});
}
const fetchSettings = async (token: string) => {
const {data} = await axios.get("/api/server/servers", {headers: {Authorization: `Bearer ${token}`}});
return data.data;
};

View File

@@ -13,6 +13,11 @@ export function getSettings(token: string) {
const fetchSettings = async (token: string) => {
const {data} = await axios.get("/api/server/settings", {headers: {Authorization: `Bearer ${token}`}});
return data.data;
// if we are not localhost ignore the devDir setting.
const url: string = window.location.host.split(":")[0];
let settingsData = data.data;
if (url != "localhost") {
settingsData.filter((n: any) => n.name === "devDir");
}
return settingsData;
};

View File

@@ -13,14 +13,13 @@ export function hasAccess(user: User | null, moduleName: string | null, modules:
}
export function hasPageAccess(user: User | null, role: any, module_id: string): boolean {
if (role.includes("viewer")) return true;
if (!user) return false;
// get only the module in the user profile
const userRole = user?.roles.filter((role) => role.module_id === module_id);
if (role.includes(userRole[0]?.role)) {
return true;
}
if (role.includes(userRole[0]?.role)) return true;
return false;
}

View File

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

20252
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +1,81 @@
{
"name": "lstv2",
"version": "2.5.1",
"type": "module",
"scripts": {
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
"dev:server": "dotenvx run -f .env -- tsx watch server/index.ts",
"dev:frontend": "cd frontend && npm run dev",
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
"build": "npm run build:server && npm run build:frontend",
"build:server": "rimraf build && tsc --build && xcopy server\\scripts dist\\server\\scripts /E /I /Y",
"build:frontend": "cd frontend && npm run build",
"start": "set NODE_ENV=production && npm run start:server",
"start:server": "dotenvx run -f .env -- node dist/server/index.js",
"db:generate": "npx drizzle-kit generate",
"db:migrate": "npx drizzle-kit push",
"deploy": "standard-version --conventional-commits && npm run prodBuild",
"zipServer": " tsx server/scripts/zipUpBuild.ts \"C:\\Users\\matthes01\\Documents\\lstv2\"",
"prodBuild": "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"
},
"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",
"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"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"admConfig": {
"build": 9,
"oldBuild": "backend-0.1.2-217.zip"
"name": "lstv2",
"version": "2.9.0",
"type": "module",
"scripts": {
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
"dev:server": "dotenvx run -f .env -- tsx watch server/index.ts",
"dev:frontend": "cd frontend && npm run dev",
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
"build": "npm run build:server && npm run build:frontend",
"build:server": "rimraf dist && tsc --build && npm run copy:scripts",
"build:frontend": "cd frontend && npm run build",
"copy:scripts": "tsx server/scripts/copyScripts.ts",
"copy:servers": "xcopy server\\services\\server\\utils\\serverData.json dist\\server\\services\\server\\utils /E /I /Y",
"start": "set NODE_ENV=production && npm run start:server",
"start:server": "dotenvx run -f .env -- node dist/server/index.js",
"db:generate": "npx drizzle-kit generate",
"db:migrate": "npx drizzle-kit push",
"db:dev": "npm run build && npm run db:generate && npm run db:migrate",
"deploy": "standard-version --conventional-commits && npm run prodBuild",
"zipServer": "dotenvx run -f .env -- tsx server/scripts/zipUpBuild.ts \"C:\\Users\\matthes01\\Documents\\lstv2\"",
"v1Build": "cd C:\\Users\\matthes01\\Documents\\logisticsSupportTool && npm run oldBuilder",
"prodBuild": "npm run v1Build && powershell -ExecutionPolicy Bypass -File server/scripts/build.ps1 -dir \"C:\\Users\\matthes01\\Documents\\lstv2\" && npm run zipServer",
"commit": "cz",
"prodinstall": "npm i --omit=dev && npm run db:migrate",
"checkupdates": "npx npm-check-updates"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
},
"admConfig": {
"build": 50,
"oldBuild": "backend-0.1.3.zip"
},
"devDependencies": {
"@types/adm-zip": "^0.5.7",
"@types/bcrypt": "^5.0.2",
"@types/fs-extra": "^11.0.4",
"@types/js-cookie": "^3.0.6",
"@types/mssql": "^9.1.7",
"@types/node": "^22.13.11",
"@types/pg": "^8.11.11",
"@types/ws": "^8.18.0",
"concurrently": "^9.1.2",
"cz-conventional-changelog": "^3.3.0",
"drizzle-kit": "^0.30.5",
"fs-extra": "^11.3.0",
"standard-version": "^9.5.0",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
},
"dependencies": {
"@dotenvx/dotenvx": "^1.39.0",
"@hono/node-server": "^1.14.0",
"@hono/zod-openapi": "^0.19.2",
"@scalar/hono-api-reference": "^0.7.2",
"@types/jsonwebtoken": "^9.0.9",
"adm-zip": "^0.5.16",
"axios": "^1.8.4",
"bcryptjs": "^3.0.2",
"date-fns": "^4.1.0",
"drizzle-orm": "^0.41.0",
"drizzle-zod": "^0.7.0",
"fast-xml-parser": "^5.0.9",
"jsonwebtoken": "^9.0.2",
"mssql": "^11.0.1",
"nodemailer": "^6.10.0",
"nodemailer-express-handlebars": "^7.0.0",
"pg": "^8.14.1",
"pino": "^9.6.0",
"pino-abstract-transport": "^2.0.0",
"pino-pretty": "^13.0.0",
"postgres": "^3.4.5",
"rimraf": "^6.0.1",
"ws": "^8.18.1",
"zod": "^3.24.2"
}
}

View File

@@ -0,0 +1,3 @@
export const delay = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};

View File

@@ -0,0 +1,8 @@
export const apiOptions = () => {
return {
tags: ["rfid"],
summary: "Add new reader",
method: "post",
path: "/addreader",
};
};

View File

@@ -0,0 +1,45 @@
import {z} from "@hono/zod-openapi";
const responseSchema = z.object({
success: z.boolean().openapi({example: true}),
message: z.string().optional(),
data: z
.array(z.object({}).optional())
.optional()
.openapi({example: [{data: "hi"}]}),
});
export const responses = () => {
return {
200: {
content: {
"application/json": {schema: responseSchema},
},
description: "Response message",
},
400: {
content: {
"application/json": {
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
},
},
description: "Internal Server Error",
},
401: {
content: {
"application/json": {
schema: z.object({message: z.string().optional().openapi({example: "Unauthenticated"})}),
},
},
description: "Unauthorized",
},
500: {
content: {
"application/json": {
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
},
},
description: "Internal Server Error",
},
};
};

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 {serveStatic} from "@hono/node-server/serve-static";
import {logger} from "hono/logger";
import {cors} from "hono/cors";
import {createLog} from "./services/logger/logger.js";
import {closePool} from "./services/sqlServer/prodSqlServer.js";
import { serve } from "@hono/node-server";
import { OpenAPIHono } from "@hono/zod-openapi";
import { proxy } from "hono/proxy";
import { serveStatic } from "@hono/node-server/serve-static";
import { logger } from "hono/logger";
import { cors } from "hono/cors";
import { createLog } from "./services/logger/logger.js";
import { WebSocketServer } from "ws";
// custom routes
import scalar from "./services/general/route/scalar.js";
import system from "./services/server/systemServer.js";
@@ -16,10 +16,12 @@ import ocme from "./services/ocme/ocmeService.js";
import sqlService from "./services/sqlServer/sqlService.js";
import logistics from "./services/logistics/logisticsService.js";
import rfid from "./services/rfid/rfidService.js";
import {db} from "../database/dbclient.js";
import {settings} from "../database/schema/settings.js";
import {count, eq} from "drizzle-orm";
import printers from "./services/printers/printerService.js";
import loggerService from "./services/logger/loggerService.js";
import ocpService from "./services/ocp/ocpService.js";
import { db } from "../database/dbclient.js";
import { settings } from "../database/schema/settings.js";
import { count } from "drizzle-orm";
// create the main prodlogin here
const username = "lst_user";
@@ -27,115 +29,148 @@ const password = "Alpla$$Prod";
export const lstAuth = btoa(`${username}:${password}`);
// checking to make sure we have the settings intialized
const serverIntialized = await db.select({count: count()}).from(settings);
export const installed = serverIntialized[0].count === 0 && process.env.NODE_ENV !== "development" ? false : true;
const serverIntialized = await db.select({ count: count() }).from(settings);
export const installed =
serverIntialized[0].count === 0 && process.env.NODE_ENV !== "development"
? false
: true;
createLog("info", "LST", "server", `Server is installed: ${installed}`);
const allowedOrigins = [
"http://localhost:3000",
"http://localhost:4000",
"http://localhost:5173",
`http://usmcd1vms006:4000`,
];
const app = new OpenAPIHono();
const app = new OpenAPIHono({ strict: false });
// middle ware
app.use("*", logger());
app.use(
"*",
cors({
origin: allowedOrigins,
allowHeaders: ["X-Custom-Header", "Upgrade-Insecure-Requests"],
allowMethods: ["POST", "GET", "OPTIONS"],
exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
maxAge: 600,
credentials: true,
})
"*",
cors({
origin: "*", // Allow all origins
allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
//exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
credentials: true, // Allow credentials if needed
maxAge: 600,
})
);
// Middleware to normalize route case
app.use("*", async (c, next) => {
const lowercasedUrl = c.req.url.toLowerCase();
const lowercasedUrl = c.req.url.toLowerCase();
//console.log("Incoming Request:", c.req.url, c.req.method);
// If the URL is already lowercase, continue as usual
if (c.req.url === lowercasedUrl) {
return next();
}
// If the URL is already lowercase, continue as usual
if (c.req.url === lowercasedUrl) {
return next();
}
// Otherwise, re-route internally
return c.redirect(lowercasedUrl, 308); // 308 preserves the HTTP method
// Otherwise, re-route internally
return c.redirect(lowercasedUrl, 308); // 308 preserves the HTTP method
});
app.doc("/api/ref", {
openapi: "3.0.0",
info: {
version: "2.0.0",
title: "LST API",
},
openapi: "3.0.0",
info: {
version: "2.0.0",
title: "LST API",
},
});
const routes = [
scalar,
auth,
// apiHits,
system,
tcpServer,
sqlService,
logistics,
rfid,
scalar,
auth,
// apiHits,
system,
tcpServer,
sqlService,
logistics,
rfid,
printers,
loggerService,
ocpService,
] as const;
const appRoutes = routes.forEach((route) => {
app.route("/api/", route);
app.route("/api/", route);
});
// the catch all api route
app.all("/api/*", (c) => c.json({error: "API route not found"}, 404));
app.route("/ocme/", ocme);
// async (c) => {
// //return ocmeService(c);
// c.json({error: "Ocme route not found"}, 404);
//--------------- lst v1 proxy ----------------------\\
// app.all("/api/v1/*", (c) => {
// const path = c.req.path.replace("/api/v1/", ""); // Extract the subpath
// const query = c.req.query() ? "?" + new URLSearchParams(c.req.query()).toString() : ""; // Get query params
// return proxy(`http://localhost:4900/${path}${query}`, {
// headers: {
// ...c.req.header(),
// "X-Forwarded-For": "127.0.0.1",
// "X-Forwarded-Host": c.req.header("host"),
// },
// });
// });
// app.all("/system/*", (c) => {
// const path = c.req.path.replace("/system/", ""); // Extract the subpath
// const query = c.req.query() ? "?" + new URLSearchParams(c.req.query()).toString() : ""; // Get query params
// return proxy(`http://localhost:4200/${path}${query}`, {
// headers: {
// ...c.req.header(),
// "X-Forwarded-For": "127.0.0.1",
// "X-Forwarded-Host": c.req.header("host"),
// },
// });
// });
//---------------------------------------------------\\
// the catch all api route
app.all("/api/*", (c) => c.json({ error: "API route not found" }, 404));
// front end static files
app.use("/*", serveStatic({root: "./frontend/dist"}));
app.use("*", serveStatic({path: "./frontend/dist/index.html"}));
app.use("/*", serveStatic({ root: "./frontend/dist" }));
app.use("*", serveStatic({ path: "./frontend/dist/index.html" }));
// Handle app exit signals
process.on("SIGINT", async () => {
console.log("\nGracefully shutting down...");
//await closePool();
process.exit(0);
console.log("\nGracefully shutting down...");
//await closePool();
process.exit(0);
});
process.on("SIGTERM", async () => {
console.log("Received termination signal, closing database...");
//await closePool();
process.exit(0);
console.log("Received termination signal, closing database...");
//await closePool();
process.exit(0);
});
process.on("uncaughtException", async (err) => {
console.log("Uncaught Exception:", err);
//await closePool();
process.exit(1);
console.log("Uncaught Exception:", err);
//await closePool();
process.exit(1);
});
process.on("beforeExit", async () => {
console.log("Process is about to exit...");
//await closePool();
console.log("Process is about to exit...");
//await closePool();
process.exit(0);
});
const port =
process.env.NODE_ENV === "development"
? process.env.VITE_SERVER_PORT
: process.env.PROD_PORT;
serve(
{
fetch: app.fetch,
port: Number(process.env.VITE_SERVER_PORT),
hostname: "0.0.0.0",
},
(info) => {
createLog("info", "LST", "server", `Server is running on http://${info.address}:${info.port}`);
}
{
fetch: app.fetch,
port: Number(port),
hostname: "0.0.0.0",
},
(info) => {
createLog(
"info",
"LST",
"server",
`Server is running on http://${info.address}:${info.port}`
);
}
);
export type AppRoutes = typeof appRoutes;

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,13 +68,13 @@ JWT_SECRET=CHANGEME
JWT_REFRESH_SECRET=CHANGEME
# Expire info plus refresh change as needed
JWT_EXPIRES=60
JWT_EXPIRES=60
JWT_REFRESH_THRESHOLD=30
JWT_ACCESS_EXPIRATION="1h"
JWT_REFRESH_EXPIRATION="7d"
# this code will need to be used when a user needs to have access to everything.
SECRETOVERRIDECODE="mVSDCpBdxreIJ979ziI71GRubBc2mqVqvZdfA22CB7smBfqlE9S3rKTE909yCHte"
SECRETOVERRIDECODE="supersecretKey"
# Database url - please change the password if this is all you changed
DATABASE_URL="postgresql://postgres:PASSWORD@localhost:5432/lst_db"
@@ -64,62 +85,80 @@ MAXLOTS=3
```
### Run the start command to get all the basic settings and modules installed
1. Run the below
```bash
npm start
```
This command will start up the server and seed the database.
* Settings will be set here.
* All modules will be added.
- Settings will be set here.
- All modules will be added.
2. Press CTRL + C to stop the server.
3. Reopen postgres and review the settings make the changes to match the server your going to be running in.
* Change the server
* change the dbServer
* change plantToken
* then the remaining settings confirm if you need on or want to leave as default.
- Change the server
- change the dbServer
- change plantToken
- then the remaining settings confirm if you need on or want to leave as default.
### Creating first user.
1. Start the server back up.
```bash
npm start
```
2. Open http://[SERVER]:[PORT]/api/docs or postman and create a user.
* Please do not try to manually enter a new user this is due to how the password is hashed, as well as setting systemAdmin for the first user.
* Change the server and port to what you changed in the DB.
- Please do not try to manually enter a new user this is due to how the password is hashed, as well as setting systemAdmin for the first user.
- Change the server and port to what you changed in the DB.
3. Stop the server again with CTRL + C.
### Running as a serivice.
You want to CD into the scripts folder.
```bash
cd .\dist\server\scripts\
```
Next use the example command below to get the service up and running.
* Options legend
* serviceName = not recommended to change to reduce issues with the update process
* option = use install for the install, but you can use this script later to stop, start, restart the service.
* appPath = where did you extract the server files
* description = no need to change this unless you want it to be something else
* command = do not change this unless you know what your doing and really need to change this.
- Options legend
- serviceName = not recommended to change to reduce issues with the update process
- option = use install for the install, but you can use this script later to stop, start, restart the service.
- appPath = where did you extract the server files
- description = no need to change this unless you want it to be something else
- command = do not change this unless you know what your doing and really need to change this.
```powershell
.\services.ps1 -serviceName "LSTV2" -option "install" -appPath "E:\LST\lstV2" -description "Logistics Support Tool V2" -command "run start"
```
# Migrating From V1 to V2
## User migration
1. Open the sqlite db and export to sql the users table
2. OPen the sql in notepad++ or your editor of choice and change the query to be similar to below.
* we only need to have save the username, role, email, password
- we only need to have save the username, role, email, password
An example new query will look like
* Below is how it looks when exported from sqlite
- Below is how it looks when exported from sqlite
```sql
INSERT INTO "User" ("id", "username", "email", "role", "password", "passwordToken", "tokenExpire", "active", "pinCode", "lastLogin", "add_user", "add_date", "upd_user", "upd_date") VALUES
(1, 'matthes01', 'blake.matthes@alpla.com', 'admin', 'JDJiJDEMUJEdGtL', NULL, NULL, '1', NULL, '1721075647687', 'LST_System', '1721075647687', 'LST_System', '1721075647687');
```
The way we want to put recreate the query to work with the new db
* Below example
- Below example
```sql
INSERT INTO "users" ("username", "email", "role", "password") VALUES
('matthes01','blake.matthes@alpla.com','admin','JDJiJDE1FuNFpkYlk4NGdHUXpEMzlHR1BD'),
@@ -127,4 +166,12 @@ INSERT INTO "users" ("username", "email", "role", "password") VALUES
('brandon001','brandon.harry@alpla.com','manager','wdm1RSXJlZnJDYTZP');
;
```
* You could have many users and just add like above with the identical info from the db
- You could have many users and just add like above with the identical info from the db
## Running v1 along Side V2 for the interm
- change v2 prod port to 4000 in the env and db
- change v1 env to 4400 in the env. and in the db you will need to change the auth server to 4000 and the serverPort to 4400
This will change so that v2 is the main server now, this is needed for ocme mainly.

View File

@@ -172,21 +172,28 @@ $plantFunness = {
Write-Host "Removing services that are no longer used."
& $nssmPath remove "LogisticsSupportTool" confirm
& $nssmPath remove $serviceAuth confirm
# & $nssmPath remove $serviceGateway confirm
# if($token -eq "usday1"){
# & $nssmPath remove $serviceOcme confirm
# }
Start-Sleep -Seconds 5
## adding in lstAdm
Write-Host "Adding $($serviceLstV2)... incase its missing."
$commandToRun = "run start"
$description = "logistics Support Tool"
& $nssmPath install $serviceLstV2 $npmPath $commandToRun
Write-Host "Setting the app directory"
& $nssmPath set $serviceLstV2 AppDirectory $appPath
Write-Host "Setting the description"
& $nssmPath set $serviceLstV2 Description $description
Write-Host "Setting recovery options"
# Set recovery options
sc.exe failure $serviceLstV2 reset= 0 actions= restart/5000/restart/5000/restart/5000
$service = Get-Service -Name $serviceLstV2 -ErrorAction SilentlyContinue
if(-not $service){
## adding in lstAdm
Write-Host "Adding $($serviceLstV2)... incase its missing."
$commandToRun = "run start"
$description = "logistics Support Tool"
& $nssmPath install $serviceLstV2 $npmPath $commandToRun
Write-Host "Setting the app directory"
& $nssmPath set $serviceLstV2 AppDirectory $appPath
Write-Host "Setting the description"
& $nssmPath set $serviceLstV2 Description $description
Write-Host "Setting recovery options"
# Set recovery options
sc.exe failure $serviceLstV2 reset= 0 actions= restart/5000/restart/5000/restart/5000
}
# Doing an install
Write-Host "Running the install to make sure everything is updated."
Set-Location $serverPath
@@ -197,12 +204,185 @@ $plantFunness = {
###########################################################
# Old system still active until we have everything off it
###########################################################
###########################################################
# Frontend env
###########################################################
Write-Host "Creating the env file in the front end"
$envContentTemplatef = @"
NEXTAUTH_SECRET= "12348fssad5sdg2f2354afvfw34"
NEXTAUTH_URL_INTERNAL= "http://localhost:3000"
NEXTAUTH_URL="{url}"
API_KEY= "E3ECD3619A943B98C6F33E3322362"
"@
try {
$url = "http://$($token)vms006:3000"
if ($token -eq "usiow2") {
$url = "http://usiow1vms006:3001"
}
if ($token -in @("test1", "test2", "test3")) {
$url = "http://usmcd1vms036:3000"
}
# Replace {url} with the actual $url
$envContentf = $envContentTemplatef -replace "{url}", $url
# Define the path where the .env file should be created
$envFilePathf = $obslst + "\apps\frontend\.env"
Write-Host "Final URL: $url"
# Write the content to the .env file
$envContentf | Out-File -FilePath $envFilePathf -Encoding UTF8 -Force
# Optional: Verify the file was created
if (Test-Path $envFilePathf) {
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathf"
} else {
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
}
} catch {
Write-Host "Error: Failed to create `.env` file on $server - $_"
}
###########################################################
# DB env
###########################################################
Write-Host "Creating the env file in the front end"
$envContentTemplateb = @"
DATABASE_URL="file:E:\LST\db\{dbLink}.db"
"@
try {
$dbLink = "lstBackendDB"
if ($token -eq "usiow2") {
$dbLink = "lstBackendDB_2"
}
if ($token -in @("test1", "test2", "test3")) {
$dbLink = "lstBackendDB"
}
# Replace {url} with the actual $url
$envContentb = $envContentTemplateb -replace "{dbLink}", $dbLink
# Define the path where the .env file should be created
$envFilePathb = $obslst + "\packages\database\.env"
# Write the content to the .env file
$envContentb | Out-File -FilePath $envFilePathb -Encoding UTF8 -Force
# Optional: Verify the file was created
if (Test-Path $envFilePathb) {
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathb"
} else {
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
}
} catch {
Write-Host "Error: Failed to create `.env` file on $server - $_"
}
###########################################################
# backend env
###########################################################
Write-Host "Creating the env file in the front end"
$envContentTemplated = @"
# Server env
NODE_ENV = production
# server apiKey
API_KEY = E3ECD3619A943B98C6F33E3322362
# Prisma DB link
DATABASE_URL="file:E:\LST\db\{dbLink}.db"
# if you still want the db in the same folder as the server install you need to do like the example below else use the relevent link
DATEBASE_LOC="E:\LST\db\{dbLink}.db"
DATABASE_BACKUP_LOC="E:\LST\backups"
# Server port
GATEWAY_PORT={gatewayport}
AUTH_PORT=4100
SYSTEM_APP_PORT={systemport}
OCME_PORT={ocme}
# This should me removed once we have the entire app broke out to its own apps
OLD_APP_PORT={appPort}
# Logging
LOG_LEVEL = info
LOG_LOC ="E:\\LST\\logs"
# authentication
SALTING = 12
SECRET = E3ECD3619A943B98C6F33E3322362
JWT_SECRET = 12348fssad5sdg2f2354afvfw34
JWT_EXPIRES_TIME = 1h
# cookie time is in min please take this into consideration when creating all the times
COOKIE_EXPIRES_TIME = 60
# password token reset in mintues
RESET_TOKEN = 330
"@
try {
$dbLink = "lstBackendDB"
$gatewayport = "4400"
$systemport = "4200"
$ocmeport = "4300"
$appport = "4900"
if ($token -eq "usiow2") {
$dbLink = "lstBackendDB_2"
$gatewayport = "4401"
$systemport = "4201"
$ocmeport = "4301"
$appport = "4901"
}
if ($token -in @("test1", "test2", "test3")) {
$dbLink = "lstBackendDB"
}
#
$port1 = $envContentTemplated -replace "{gatewayport}", $gatewayport
$port2 = $port1 -replace "{systemport}", $systemport
$port3 = $port2 -replace "{ocme}", $ocmeport
$port4 = $port3 -replace "{appPort}", $appport
$envContentd = $port4 -replace "{dbLink}", $dbLink
# Define the path where the .env file should be created
$envFilePathd = $obslst + "\.env"
# Write the content to the .env file
$envContentd | Out-File -FilePath $envFilePathd -Encoding UTF8 -Force
# Optional: Verify the file was created
if (Test-Path $envFilePathd) {
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathd"
} else {
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
}
} catch {
Write-Host "Error: Failed to create `.env` file on $server - $_"
}
Write-Host "Running install on obs server."
Set-Location $obslst
npm run newinstall # --omit=dev
Write-Host "Update the frontend"
npm run install:front
npm run install:ui
npm run install:db
Write-Host "Running db updates"
npm run db:migrate
@@ -235,10 +415,9 @@ $plantFunness = {
Start-Service -DisplayName $serviceLstV2
Start-Sleep -Seconds 1
Write-Host "$($server) finished updating"
if($token -eq "usday1"){
Write-Host "Starting $($serviceOcme)"
Start-Service -DisplayName $serviceOcme
Start-Service -DisplayName $serviceOcme
}
}

View File

@@ -5,9 +5,43 @@ import {serverData} from "../../database/schema/serverData.js";
import {eq, sql} from "drizzle-orm";
import {createLog} from "../services/logger/logger.js";
const updateServer = async (devApp: string, server: string) => {
type UpdateServerResponse = {
success: boolean;
message: string;
};
export const updateServer = async (devApp: string, server: string | null): Promise<UpdateServerResponse> => {
const app = await getAppInfo(devApp);
const serverInfo = await db.select().from(serverData).where(eq(serverData.sName, server.toLowerCase()));
const serverInfo = await db
.select()
.from(serverData)
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
if (serverInfo.length === 0) {
createLog(
"error",
"lst",
"serverUpdater",
`Looks like you are missing the plant token or have entered an incorrect one please try again.`
);
return {
success: false,
message: "Looks like you are missing the plant token or have entered an incorrect one please try again.",
};
}
if (serverInfo[0].isUpgrading) {
createLog(
"error",
"lst",
"serverUpdater",
`Looks like ${serverInfo[0].plantToken} is upgrading already you cant do this again.`
);
return {
success: false,
message: `Looks like ${serverInfo[0].plantToken} is upgrading already you cant do this again.`,
};
}
const scriptPath = `${process.env.DEVFOLDER}\\server\\scripts\\update.ps1 `;
const args = [
@@ -39,9 +73,13 @@ const updateServer = async (devApp: string, server: string) => {
,
];
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
const process = spawn("powershell", args);
// change the server to upgradeing
await db
.update(serverData)
.set({isUpgrading: true})
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
//let stdout = "";
//let stderr = "";
@@ -69,7 +107,16 @@ const updateServer = async (devApp: string, server: string) => {
//update the last build.
try {
await db.update(serverData).set({lastUpdated: sql`NOW()`});
await db
.update(serverData)
.set({lastUpdated: sql`NOW()`, isUpgrading: false})
.where(eq(serverData.plantToken, server?.toLowerCase() ?? ""));
createLog(
"info",
"lst",
"serverUpdater",
`${server?.toLowerCase()}, has been updated and can now be used again.`
);
} catch (error) {
createLog(
"error",
@@ -79,7 +126,10 @@ const updateServer = async (devApp: string, server: string) => {
);
}
resolve({success: true, code});
resolve({
success: true,
message: `${server?.toLowerCase()}, has been updated and can now be used again.`,
});
} else {
const errorMessage = `Process exited with code ${code}`;
@@ -87,7 +137,10 @@ const updateServer = async (devApp: string, server: string) => {
// //onClose(code);
// }
reject(new Error(errorMessage));
reject({
success: false,
message: `${server?.toLowerCase()}, Has encounted an error while updating.`,
});
}
});
@@ -107,7 +160,7 @@ export async function processAllServers(devApp: string) {
let count = 1;
for (const server of servers) {
try {
const updateToServer = await updateServer(devApp, server.sName);
const updateToServer = await updateServer(devApp, server.plantToken);
createLog("info", "lst", "serverUpdater", `${server.sName} was updated.`);
count = count + 1;
@@ -118,5 +171,3 @@ export async function processAllServers(devApp: string) {
}
}
}
updateServer("C:\\Users\\matthes01\\Documents\\lstv2", "test");

View File

@@ -1,147 +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",
"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",
".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.includes(normalizedIgnorePatther);
});
return ignoreList.some((ignorePattern) => {
const normalizedIgnorePatther = ignorePattern.replace(/\\/g, "/");
return (
normalizedItemPath === normalizedIgnorePatther ||
normalizedItemPath.startsWith(`${normalizedIgnorePatther}/`)
);
});
};
const addToZip = (zip: any, currentPath: any, rootPath: any) => {
const items = fs.readdirSync(currentPath);
const addToZip = (zip: any, currentPath: string, rootPath: string) => {
const items = fs.readdirSync(currentPath);
items.forEach((item) => {
const itemPath = path.join(currentPath, item);
const relativePath = path.relative(rootPath, itemPath);
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 = app.admConfig.dest;
//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, app.admConfig.src, app.admConfig.src);
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");
@@ -149,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

@@ -5,6 +5,7 @@ import {eq, sql} from "drizzle-orm";
import {checkPassword} from "../utils/checkPassword.js";
import {roleCheck} from "./userRoles/getUserAccess.js";
import {createLog} from "../../logger/logger.js";
import {differenceInDays} from "date-fns";
/**
* Authenticate a user and return a JWT.
@@ -38,7 +39,7 @@ export async function login(
user_id: user[0].user_id,
username: user[0].username,
email: user[0].email,
roles: roles || null,
//roles: roles || null,
role: user[0].role || null, // this should be removed onces full migration to v2 is completed
prod: btoa(`${username.toLowerCase()}:${password}`),
};
@@ -50,7 +51,14 @@ export async function login(
.set({lastLogin: sql`NOW()`})
.where(eq(users.user_id, user[0].user_id))
.returning({lastLogin: users.lastLogin});
createLog("info", "lst", "auth", `Its been 5days since ${user[0].username} has logged in`);
createLog(
"info",
"lst",
"auth",
`Its been ${differenceInDays(lastLog[0]?.lastLogin ?? "", new Date(Date.now()))} days since ${
user[0].username
} has logged in`
);
//]);
} catch (error) {
createLog("error", "lst", "auth", "There was an error updating the user last login");

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;

Some files were not shown because too many files have changed in this diff Show More