From f35fcbc875cc967976925505ecb5d858fbf6aec7 Mon Sep 17 00:00:00 2001 From: fabio Date: Mon, 27 Apr 2026 22:37:59 +0200 Subject: [PATCH] feat: add signup page, authentication middleware, user password update functionality, and roles API endpoint - Created a new signup page in the frontend application. - Implemented authentication middleware to validate access tokens and retrieve claims. - Added functionality to update user passwords and revoke sessions in the user repository. - Introduced an API endpoint to fetch user roles with TypeScript support. Co-authored-by: Copilot --- .DS_Store | Bin 6148 -> 6148 bytes backend/.DS_Store | Bin 6148 -> 6148 bytes backend/.env | 2 +- backend/GeneratedCode/admin.ts | 36 -- backend/GeneratedCode/api.ts | 276 --------------- backend/GeneratedCode/apiTypes.ts | 4 - backend/GeneratedCode/responses.ts | 3 - backend/GeneratedCode/systemUtils.ts | 46 --- backend/GeneratedCode/tokens.ts | 4 - backend/GeneratedCode/users.ts | 234 ------------- backend/cmd/server/main.go | 13 +- backend/data/data.db | Bin 946176 -> 81920 bytes backend/internal/admin/controller.go | 16 +- backend/internal/admin/routes.go | 13 +- backend/internal/auth/roles.go | 97 ++++++ backend/internal/auth/routes.go | 8 + .../internal/http/static/spa/about/index.html | 3 + .../spa/assets/AboutUsPage-BH0yEEbx.css | 1 + .../static/spa/assets/AboutUsPage-DcX9GDFi.js | 1 + .../static/spa/assets/AdminLayout-C0x3OCjv.js | 1 + .../spa/assets/ApiEndpointsPage-CebWC3ON.js | 1 + .../spa/assets/ApiEndpointsPage-Chy729e4.css | 1 + .../static/spa/assets/ClosePopup-B8Y1XOF4.js | 1 + .../spa/assets/ContactUsPage-B5Y6EbXq.css | 1 + .../spa/assets/ContactUsPage-DgQQfHWn.js | 1 + .../static/spa/assets/DevLayout-BakHV7CK.js | 1 + .../spa/assets/DoctorDetailPage-Cjwb6JKF.css | 1 + .../spa/assets/DoctorDetailPage-DHHIacjh.js | 1 + .../spa/assets/DoctorsPage-DHzbb8aG.css | 1 + .../static/spa/assets/DoctorsPage-DZ_PCxTw.js | 1 + .../spa/assets/ErrorNotFound-C3p6pofJ.js | 1 + .../static/spa/assets/HomeHeader-CzzvZK70.css | 1 + .../static/spa/assets/HomeHeader-ymaaGHJk.js | 1 + .../static/spa/assets/IndexPage-CBJ3Xsw8.js | 1 + .../static/spa/assets/IndexPage-CqJqqT8_.js | 1 + .../static/spa/assets/IndexPage-DQmOg4ht.js | 1 + .../static/spa/assets/IndexPage-DnAgFSQ-.css | 1 + .../static/spa/assets/IndexPage-JYcDosFI.css | 1 + ...ylUAMQXC89YmC2DPNWuYjalmUiAw-BepdiOnY.woff | Bin 0 -> 25552 bytes ...ylUAMQXC89YmC2DPNWuZtalmUiAw-4ZhHFPot.woff | Bin 0 -> 25260 bytes ...ylUAMQXC89YmC2DPNWuaabVmUiAw-CNa4tw4G.woff | Bin 0 -> 25368 bytes ...ylUAMQXC89YmC2DPNWub2bVmUiAw-CHKg1YId.woff | Bin 0 -> 25396 bytes ...ylUAMQXC89YmC2DPNWubEbFmUiAw-yBxCyPWP.woff | Bin 0 -> 24924 bytes ...ylUAMQXC89YmC2DPNWubEbVmUiAw-3fZ6d7DD.woff | Bin 0 -> 25192 bytes .../static/spa/assets/LoginPage-C47YrH6r.js | 1 + .../static/spa/assets/LoginPage-Dnsd-oBb.css | 1 + .../spa/assets/MailDebugPage-CSGJU856.css | 1 + .../spa/assets/MailDebugPage-DxfFYsI5.js | 1 + .../static/spa/assets/MainLayout-B3XVNNcF.js | 1 + .../static/spa/assets/MainLayout-BV33WPCg.css | 1 + .../static/spa/assets/QDrawer-vTuTpDsu.js | 1 + .../http/static/spa/assets/QForm-DxIW6oMr.js | 1 + .../static/spa/assets/QLayout--H1lCa8l.js | 1 + .../spa/assets/QLinearProgress-DbNJvTIO.js | 1 + .../http/static/spa/assets/QPage-AlxqRIFS.js | 1 + .../spa/assets/QResizeObserver-CsRw6Xhi.js | 1 + .../static/spa/assets/QSelect-CczoW0Cf.js | 1 + .../static/spa/assets/QToolbar-CPqQ-3uW.js | 1 + .../assets/RecoverPasswordPage-Cq-W2sgM.css | 1 + .../assets/RecoverPasswordPage-jRZMIG56.js | 1 + .../spa/assets/ResetPasswordPage-BwO0iv2-.css | 1 + .../spa/assets/ResetPasswordPage-CGvyuJpw.js | 1 + .../spa/assets/ServicesPage-DAWy2Xhp.css | 1 + .../spa/assets/ServicesPage-DtpiTumh.js | 1 + .../static/spa/assets/SignupPage-BDtIqoZR.css | 1 + .../static/spa/assets/SignupPage-DBfV2ifb.js | 1 + .../static/spa/assets/UsersPage-C5DG2fsw.css | 1 + .../static/spa/assets/UsersPage-IbPn6tyc.js | 2 + .../_plugin-vue_export-helper-DlAUqK2U.js | 1 + .../spa/assets/about-img-1-Bg9mkIhK.jpg | Bin 0 -> 324696 bytes .../spa/assets/about-img-2-BboiNeez.jpg | Bin 0 -> 85991 bytes .../http/static/spa/assets/admin-CJkezuRb.js | 1 + .../http/static/spa/assets/api-5Y4dWpBS.js | 1 + .../static/spa/assets/cta-img-1-Bi1YsVtQ.png | Bin 0 -> 70186 bytes .../static/spa/assets/cta-img-2-Brfqav_y.png | Bin 0 -> 144166 bytes ...flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff | Bin 0 -> 164912 bytes ...tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 | Bin 0 -> 128616 bytes .../http/static/spa/assets/format-CAeFuMoJ.js | 1 + .../static/spa/assets/hero-img-D6ekzwy-.png | Bin 0 -> 458632 bytes .../http/static/spa/assets/i18n-_xI72VMJ.js | 1 + .../spa/assets/icon-about-info-2-xhkDUeRt.svg | 16 + .../spa/assets/icon-about-info-3-DWJgo5gs.svg | 16 + .../spa/assets/icon-faq-cta-HccIooJi.svg | 9 + .../spa/assets/icon-service-2-CyvmgH6D.svg | 12 + .../http/static/spa/assets/index-CLvovu40.js | 4 + .../http/static/spa/assets/index-CxY4d-3p.css | 1 + .../static/spa/assets/instagram-t8lEWnnA.svg | 1 + .../http/static/spa/assets/logo-7g001S5W.png | Bin 0 -> 117513 bytes .../http/static/spa/assets/logo-BWWTOLPG.js | 1 + .../spa/assets/position-engine-B6qn09JM.js | 1 + .../static/spa/assets/selection-CkoEqZ5D.js | 1 + .../spa/assets/service-img-1-DW0zEdQo.jpg | Bin 0 -> 199614 bytes .../spa/assets/service-img-2-C5-3A23e.jpg | Bin 0 -> 174575 bytes .../spa/assets/service-img-3-Dqdyjcu5.jpg | Bin 0 -> 199915 bytes .../static/spa/assets/systemUtils-yS___ZlB.js | 1 + .../static/spa/assets/team-1-CKnEIDo_.jpg | Bin 0 -> 214908 bytes .../http/static/spa/assets/team-1-CMaNLVo5.js | 1 + .../static/spa/assets/team-2-tlSyplqu.jpg | Bin 0 -> 217740 bytes .../static/spa/assets/team-3-UsSQDZ5X.jpg | Bin 0 -> 213686 bytes .../http/static/spa/assets/team-4-BDlfXLz_.js | 1 + .../static/spa/assets/team-4-CKCxoRxO.jpg | Bin 0 -> 244873 bytes .../http/static/spa/assets/touch-BjYP5sR0.js | 1 + .../static/spa/assets/use-quasar-Do408P4O.js | 1 + .../http/static/spa/assets/users-DP4IbzRG.js | 1 + .../spa/assets/work-step-img-1-BTXGLo3T.jpg | Bin 0 -> 34004 bytes .../spa/assets/work-step-img-2-BmLBMPhT.jpg | Bin 0 -> 30139 bytes .../spa/assets/work-step-img-3-B1BPr4WH.jpg | Bin 0 -> 42651 bytes .../spa/assets/work-step-img-4-BbeUh9vy.jpg | Bin 0 -> 41034 bytes .../http/static/spa/contact/index.html | 3 + .../http/static/spa/doctordetails/index.html | 3 + .../http/static/spa/doctors/index.html | 3 + backend/internal/http/static/spa/favicon.ico | Bin 0 -> 64483 bytes .../http/static/spa/icons/favicon-128x128.png | Bin 0 -> 12324 bytes .../http/static/spa/icons/favicon-16x16.png | Bin 0 -> 859 bytes .../http/static/spa/icons/favicon-32x32.png | Bin 0 -> 2039 bytes .../http/static/spa/icons/favicon-96x96.png | Bin 0 -> 9643 bytes backend/internal/http/static/spa/index.html | 4 +- .../internal/http/static/spa/login/index.html | 3 + .../static/spa/recoverpassword/index.html | 3 + .../http/static/spa/services/index.html | 3 + .../http/static/spa/signup/index.html | 3 + backend/internal/middleware/auth.go | 36 ++ backend/internal/roles/main_roles.go | 9 - backend/internal/roles/permissions.go | 75 ---- backend/internal/routes/register.go | 10 +- backend/internal/seed/seed.go | 17 +- backend/internal/systemUtils/routes.go | 11 +- backend/internal/tokens/services.go | 45 +-- backend/internal/tsgenerator/generator.go | 7 +- backend/internal/user/controller.go | 99 ++++-- backend/internal/user/model.go | 119 +++---- backend/internal/user/rerpository.go | 35 ++ backend/internal/user/routes.go | 40 ++- backend/pkg/ts-rpc/TSFiles.go | 9 +- backend/pkg/ts-rpc/tsInfo.go | 30 +- backend/pkg/ts-rpc/tsStruct.go | 6 +- backend/pkg/ts-rpc/tsTsInfo.go | 1 + backend/pkg/ts-rpc/tsrpc.go | 2 +- backend/postman_collection.json | 220 ------------ frontend/src/api/admin.ts | 35 +- frontend/src/api/api.ts | 2 +- frontend/src/api/apiTypes.ts | 4 +- frontend/src/api/auth.ts | 21 ++ frontend/src/api/systemUtils.ts | 9 +- frontend/src/api/users.ts | 326 +++++++++--------- frontend/src/layouts/MainLayout.vue | 4 +- frontend/src/pages/admin/UsersPage.vue | 100 ++---- frontend/src/pages/dev/SignupPage.vue | 2 +- 148 files changed, 833 insertions(+), 1340 deletions(-) delete mode 100644 backend/GeneratedCode/admin.ts delete mode 100644 backend/GeneratedCode/api.ts delete mode 100644 backend/GeneratedCode/apiTypes.ts delete mode 100644 backend/GeneratedCode/responses.ts delete mode 100644 backend/GeneratedCode/systemUtils.ts delete mode 100644 backend/GeneratedCode/tokens.ts delete mode 100644 backend/GeneratedCode/users.ts create mode 100644 backend/internal/auth/roles.go create mode 100644 backend/internal/auth/routes.go create mode 100644 backend/internal/http/static/spa/about/index.html create mode 100644 backend/internal/http/static/spa/assets/AboutUsPage-BH0yEEbx.css create mode 100644 backend/internal/http/static/spa/assets/AboutUsPage-DcX9GDFi.js create mode 100644 backend/internal/http/static/spa/assets/AdminLayout-C0x3OCjv.js create mode 100644 backend/internal/http/static/spa/assets/ApiEndpointsPage-CebWC3ON.js create mode 100644 backend/internal/http/static/spa/assets/ApiEndpointsPage-Chy729e4.css create mode 100644 backend/internal/http/static/spa/assets/ClosePopup-B8Y1XOF4.js create mode 100644 backend/internal/http/static/spa/assets/ContactUsPage-B5Y6EbXq.css create mode 100644 backend/internal/http/static/spa/assets/ContactUsPage-DgQQfHWn.js create mode 100644 backend/internal/http/static/spa/assets/DevLayout-BakHV7CK.js create mode 100644 backend/internal/http/static/spa/assets/DoctorDetailPage-Cjwb6JKF.css create mode 100644 backend/internal/http/static/spa/assets/DoctorDetailPage-DHHIacjh.js create mode 100644 backend/internal/http/static/spa/assets/DoctorsPage-DHzbb8aG.css create mode 100644 backend/internal/http/static/spa/assets/DoctorsPage-DZ_PCxTw.js create mode 100644 backend/internal/http/static/spa/assets/ErrorNotFound-C3p6pofJ.js create mode 100644 backend/internal/http/static/spa/assets/HomeHeader-CzzvZK70.css create mode 100644 backend/internal/http/static/spa/assets/HomeHeader-ymaaGHJk.js create mode 100644 backend/internal/http/static/spa/assets/IndexPage-CBJ3Xsw8.js create mode 100644 backend/internal/http/static/spa/assets/IndexPage-CqJqqT8_.js create mode 100644 backend/internal/http/static/spa/assets/IndexPage-DQmOg4ht.js create mode 100644 backend/internal/http/static/spa/assets/IndexPage-DnAgFSQ-.css create mode 100644 backend/internal/http/static/spa/assets/IndexPage-JYcDosFI.css create mode 100644 backend/internal/http/static/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw-BepdiOnY.woff create mode 100644 backend/internal/http/static/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw-4ZhHFPot.woff create mode 100644 backend/internal/http/static/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw-CNa4tw4G.woff create mode 100644 backend/internal/http/static/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw-CHKg1YId.woff create mode 100644 backend/internal/http/static/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw-yBxCyPWP.woff create mode 100644 backend/internal/http/static/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw-3fZ6d7DD.woff create mode 100644 backend/internal/http/static/spa/assets/LoginPage-C47YrH6r.js create mode 100644 backend/internal/http/static/spa/assets/LoginPage-Dnsd-oBb.css create mode 100644 backend/internal/http/static/spa/assets/MailDebugPage-CSGJU856.css create mode 100644 backend/internal/http/static/spa/assets/MailDebugPage-DxfFYsI5.js create mode 100644 backend/internal/http/static/spa/assets/MainLayout-B3XVNNcF.js create mode 100644 backend/internal/http/static/spa/assets/MainLayout-BV33WPCg.css create mode 100644 backend/internal/http/static/spa/assets/QDrawer-vTuTpDsu.js create mode 100644 backend/internal/http/static/spa/assets/QForm-DxIW6oMr.js create mode 100644 backend/internal/http/static/spa/assets/QLayout--H1lCa8l.js create mode 100644 backend/internal/http/static/spa/assets/QLinearProgress-DbNJvTIO.js create mode 100644 backend/internal/http/static/spa/assets/QPage-AlxqRIFS.js create mode 100644 backend/internal/http/static/spa/assets/QResizeObserver-CsRw6Xhi.js create mode 100644 backend/internal/http/static/spa/assets/QSelect-CczoW0Cf.js create mode 100644 backend/internal/http/static/spa/assets/QToolbar-CPqQ-3uW.js create mode 100644 backend/internal/http/static/spa/assets/RecoverPasswordPage-Cq-W2sgM.css create mode 100644 backend/internal/http/static/spa/assets/RecoverPasswordPage-jRZMIG56.js create mode 100644 backend/internal/http/static/spa/assets/ResetPasswordPage-BwO0iv2-.css create mode 100644 backend/internal/http/static/spa/assets/ResetPasswordPage-CGvyuJpw.js create mode 100644 backend/internal/http/static/spa/assets/ServicesPage-DAWy2Xhp.css create mode 100644 backend/internal/http/static/spa/assets/ServicesPage-DtpiTumh.js create mode 100644 backend/internal/http/static/spa/assets/SignupPage-BDtIqoZR.css create mode 100644 backend/internal/http/static/spa/assets/SignupPage-DBfV2ifb.js create mode 100644 backend/internal/http/static/spa/assets/UsersPage-C5DG2fsw.css create mode 100644 backend/internal/http/static/spa/assets/UsersPage-IbPn6tyc.js create mode 100644 backend/internal/http/static/spa/assets/_plugin-vue_export-helper-DlAUqK2U.js create mode 100644 backend/internal/http/static/spa/assets/about-img-1-Bg9mkIhK.jpg create mode 100644 backend/internal/http/static/spa/assets/about-img-2-BboiNeez.jpg create mode 100644 backend/internal/http/static/spa/assets/admin-CJkezuRb.js create mode 100644 backend/internal/http/static/spa/assets/api-5Y4dWpBS.js create mode 100644 backend/internal/http/static/spa/assets/cta-img-1-Bi1YsVtQ.png create mode 100644 backend/internal/http/static/spa/assets/cta-img-2-Brfqav_y.png create mode 100644 backend/internal/http/static/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff create mode 100644 backend/internal/http/static/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 create mode 100644 backend/internal/http/static/spa/assets/format-CAeFuMoJ.js create mode 100644 backend/internal/http/static/spa/assets/hero-img-D6ekzwy-.png create mode 100644 backend/internal/http/static/spa/assets/i18n-_xI72VMJ.js create mode 100644 backend/internal/http/static/spa/assets/icon-about-info-2-xhkDUeRt.svg create mode 100644 backend/internal/http/static/spa/assets/icon-about-info-3-DWJgo5gs.svg create mode 100644 backend/internal/http/static/spa/assets/icon-faq-cta-HccIooJi.svg create mode 100644 backend/internal/http/static/spa/assets/icon-service-2-CyvmgH6D.svg create mode 100644 backend/internal/http/static/spa/assets/index-CLvovu40.js create mode 100644 backend/internal/http/static/spa/assets/index-CxY4d-3p.css create mode 100644 backend/internal/http/static/spa/assets/instagram-t8lEWnnA.svg create mode 100644 backend/internal/http/static/spa/assets/logo-7g001S5W.png create mode 100644 backend/internal/http/static/spa/assets/logo-BWWTOLPG.js create mode 100644 backend/internal/http/static/spa/assets/position-engine-B6qn09JM.js create mode 100644 backend/internal/http/static/spa/assets/selection-CkoEqZ5D.js create mode 100644 backend/internal/http/static/spa/assets/service-img-1-DW0zEdQo.jpg create mode 100644 backend/internal/http/static/spa/assets/service-img-2-C5-3A23e.jpg create mode 100644 backend/internal/http/static/spa/assets/service-img-3-Dqdyjcu5.jpg create mode 100644 backend/internal/http/static/spa/assets/systemUtils-yS___ZlB.js create mode 100644 backend/internal/http/static/spa/assets/team-1-CKnEIDo_.jpg create mode 100644 backend/internal/http/static/spa/assets/team-1-CMaNLVo5.js create mode 100644 backend/internal/http/static/spa/assets/team-2-tlSyplqu.jpg create mode 100644 backend/internal/http/static/spa/assets/team-3-UsSQDZ5X.jpg create mode 100644 backend/internal/http/static/spa/assets/team-4-BDlfXLz_.js create mode 100644 backend/internal/http/static/spa/assets/team-4-CKCxoRxO.jpg create mode 100644 backend/internal/http/static/spa/assets/touch-BjYP5sR0.js create mode 100644 backend/internal/http/static/spa/assets/use-quasar-Do408P4O.js create mode 100644 backend/internal/http/static/spa/assets/users-DP4IbzRG.js create mode 100644 backend/internal/http/static/spa/assets/work-step-img-1-BTXGLo3T.jpg create mode 100644 backend/internal/http/static/spa/assets/work-step-img-2-BmLBMPhT.jpg create mode 100644 backend/internal/http/static/spa/assets/work-step-img-3-B1BPr4WH.jpg create mode 100644 backend/internal/http/static/spa/assets/work-step-img-4-BbeUh9vy.jpg create mode 100644 backend/internal/http/static/spa/contact/index.html create mode 100644 backend/internal/http/static/spa/doctordetails/index.html create mode 100644 backend/internal/http/static/spa/doctors/index.html create mode 100644 backend/internal/http/static/spa/favicon.ico create mode 100644 backend/internal/http/static/spa/icons/favicon-128x128.png create mode 100644 backend/internal/http/static/spa/icons/favicon-16x16.png create mode 100644 backend/internal/http/static/spa/icons/favicon-32x32.png create mode 100644 backend/internal/http/static/spa/icons/favicon-96x96.png create mode 100644 backend/internal/http/static/spa/login/index.html create mode 100644 backend/internal/http/static/spa/recoverpassword/index.html create mode 100644 backend/internal/http/static/spa/services/index.html create mode 100644 backend/internal/http/static/spa/signup/index.html create mode 100644 backend/internal/middleware/auth.go delete mode 100644 backend/internal/roles/main_roles.go delete mode 100644 backend/internal/roles/permissions.go create mode 100644 backend/internal/user/rerpository.go delete mode 100644 backend/postman_collection.json create mode 100644 frontend/src/api/auth.ts diff --git a/.DS_Store b/.DS_Store index dfa9254bd9af864c8b31b22d778a8047ca1935a7..727c33a857cfbf70f9a9a9e4d7d61f47f874a037 100644 GIT binary patch delta 39 vcmZoMXfc@J&&abeU^g=(&t@JLaYoLxl;Y&1{QMlo&4R4AnKrX?{N)D#=Hv^; delta 32 ocmZoMXfc@J&&a(oU^g=(_hudzamLMdtS6Z!Hb`$~=lIJH0G}xdwEzGB diff --git a/backend/.DS_Store b/backend/.DS_Store index 6bef2e5cfe81b8596cdfe93d18ac21e800d839ce..f8e676d75e73ae9786085fe56f559312135927fd 100644 GIT binary patch delta 54 zcmZoMXfc@J&&aVcU^gQp$7CL+dLb5u6oy2G5{AUIl;Y&1{QMlo$%mLWZa&8B$TG2^ KVlz9(Uw#0!0})gJ delta 154 zcmZoMXfc@J&&atkU^gQp=VTtHdTm|?cZO7kJRmM&NMtAh(kTqi4EaDZ)iWnQIVmSU ziGhJZ0EpL5-peFDc?HulPjQ9Rh0nJcf9NWFS8W2vdQq5{675FONZwAq%Jm Z$U-y65@^=q|6s5=klCAMGdss$egIhyD6s$l diff --git a/backend/.env b/backend/.env index 3713c4e..35a7050 100644 --- a/backend/.env +++ b/backend/.env @@ -7,7 +7,7 @@ SEED=0 # Paths CONFIG_PATH=configs/config.json ROLES_CONFIG_PATH=configs/roles.json -FRONTEND_API_PATH=/Users/fabio/CODE/APP_GO_QUASAR/frontend/src/api +FRONTEND_API_PATH=/Users/fabio/CODE/omnimed/go-quasar-partial-ssr/frontend/src/api DB_driver=sqlite DB_dsn=file:./data/data.db?_foreign_keys=on diff --git a/backend/GeneratedCode/admin.ts b/backend/GeneratedCode/admin.ts deleted file mode 100644 index 8653af2..0000000 --- a/backend/GeneratedCode/admin.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { api } from "./api"; -import type { Nullable } from "./apiTypes.ts"; -import type * as users from "./users.ts"; - -// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=users.[]User - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/admin/routes.go Line: 13 -export const listUsers = async ( - data: ListUsersRequest, -): Promise<{ data: users.User[]; error: Nullable }> => { - return (await api.POST("/admin/users", data)) as { - data: users.User[]; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=users.User - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/admin/routes.go Line: 17 -export const blockUser = async ( - data: BlockUserRequest, -): Promise<{ data: users.User; error: Nullable }> => { - return (await api.PUT("/admin/users/:uuid/block", data)) as { - data: users.User; - error: Nullable; - }; -}; - -export interface ListUsersRequest { - page: number; - pageSize: number; -} - -export interface BlockUserRequest { - action: string; -} diff --git a/backend/GeneratedCode/api.ts b/backend/GeneratedCode/api.ts deleted file mode 100644 index 7eb30ae..0000000 --- a/backend/GeneratedCode/api.ts +++ /dev/null @@ -1,276 +0,0 @@ -// -// Typescript API generated from gofiber backend -// Copyright (C) 2022 - 2025 Fabio Prada -// -// This file was generated by github.com/millevolte/ts-rpc -// -// Apr 26, 2026 14:23:26 UTC -// - -export interface ApiRestResponse { - data?: unknown; - error: string | null; -} - -function isApiRestResponse(data: unknown): data is ApiRestResponse { - return ( - typeof data === "object" && - data !== null && - Object.prototype.hasOwnProperty.call(data, "data") && - Object.prototype.hasOwnProperty.call(data, "error") - ); -} - -function normalizeError(error: unknown): Error { - if (error instanceof DOMException && error.name === "AbortError") { - return new Error("api.error.timeouterror"); - } - - if (error instanceof TypeError && error.message === "Failed to fetch") { - return new Error("api.error.connectionerror"); - } - - if (error instanceof Error) { - return error; - } - - return new Error(String(error)); -} - -export default class Api { - apiUrl: string; - localStorage: Storage | null; - - constructor(apiurl: string) { - this.apiUrl = apiurl; - this.localStorage = window.localStorage; - } - - async request( - method: string, - url: string, - data: unknown, - timeout = 7000, - upload = false, - ): Promise { - const headers: { [key: string]: string } = { - "Cache-Control": "no-cache", - }; - - if (!upload) { - headers["Content-Type"] = "application/json"; - } - - if (this.localStorage) { - const auth = this.localStorage.getItem("Auth-Token"); - if (auth) { - headers["Auth-Token"] = auth; - } - } - - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeout); - const requestOptions: RequestInit = { - method, - cache: "no-store", - mode: "cors", - credentials: "include", - headers, - signal: controller.signal, - }; - - if (upload) { - requestOptions.body = data as FormData; - } else if (data !== null && data !== undefined) { - requestOptions.body = JSON.stringify(data); - } - - try { - const response = await fetch(url, requestOptions); - - if (!response.ok) { - throw new Error("api.error." + response.statusText); - } - - if (this.localStorage) { - const jwt = response.headers.get("Auth-Token"); - if (jwt) { - this.localStorage.setItem("Auth-Token", jwt); - } - } - - const responseData = (await response.json()) as unknown; - if (!isApiRestResponse(responseData)) { - throw new Error("api.error.wrongdatatype"); - } - - if (responseData.error) { - throw new Error(responseData.error); - } - - return responseData; - } catch (error: unknown) { - throw normalizeError(error); - } finally { - clearTimeout(timeoutId); - } - } - - processResult(result: ApiRestResponse): { - data: unknown; - error: string | null; - } { - if (typeof result.data !== "object") { - return { data: result.data, error: null }; - } - - if (!result.data) { - result.data = {}; - } - - return { data: result.data, error: null }; - } - - processError(error: unknown): { - data: unknown; - error: string | null; - } { - const normalizedError = normalizeError(error); - - if (normalizedError.message === "api.error.timeouterror") { - Object.defineProperty(normalizedError, "__api_error__", { - value: normalizedError.message, - writable: false, - }); - - return { data: null, error: normalizedError.message }; - } - - if (normalizedError.message === "api.error.connectionerror") { - Object.defineProperty(normalizedError, "__api_error__", { - value: normalizedError.message, - writable: false, - }); - - return { data: null, error: normalizedError.message }; - } - - return { - data: null, - error: normalizedError.message, - }; - } - - async POST( - url: string, - data: unknown, - timeout?: number, - ): Promise<{ - data: unknown; - error: string | null; - }> { - try { - const upload = url.includes("/upload/"); - const result = await this.request( - "POST", - this.apiUrl + url, - data, - timeout, - upload, - ); - - return this.processResult(result); - } catch (error: unknown) { - return this.processError(error); - } - } - - async PUT( - url: string, - data: unknown, - timeout?: number, - ): Promise<{ - data: unknown; - error: string | null; - }> { - try { - const upload = url.includes("/upload/"); - const result = await this.request( - "PUT", - this.apiUrl + url, - data, - timeout, - upload, - ); - - return this.processResult(result); - } catch (error: unknown) { - return this.processError(error); - } - } - - async GET( - url: string, - timeout?: number, - ): Promise<{ - data: unknown; - error: string | null; - }> { - try { - const result = await this.request( - "GET", - this.apiUrl + url, - null, - timeout, - ); - return this.processResult(result); - } catch (error: unknown) { - return this.processError(error); - } - } - - async DELETE( - url: string, - timeout?: number, - ): Promise<{ - data: unknown; - error: string | null; - }> { - try { - const result = await this.request( - "DELETE", - this.apiUrl + url, - null, - timeout, - ); - return this.processResult(result); - } catch (error: unknown) { - return this.processError(error); - } - } - - async UPLOAD( - url: string, - data: unknown, - timeout?: number, - ): Promise<{ - data: unknown; - error: string | null; - }> { - try { - const result = await this.request( - "POST", - this.apiUrl + url, - data, - timeout, - true, - ); - - return this.processResult(result); - } catch (error: unknown) { - return this.processError(error); - } - } -} - -export const api = new Api("http://localhost:3000"); diff --git a/backend/GeneratedCode/apiTypes.ts b/backend/GeneratedCode/apiTypes.ts deleted file mode 100644 index 4a30202..0000000 --- a/backend/GeneratedCode/apiTypes.ts +++ /dev/null @@ -1,4 +0,0 @@ -// API Declarations -export type Record = { [P in K]: T }; - -export type Nullable = T | null; diff --git a/backend/GeneratedCode/responses.ts b/backend/GeneratedCode/responses.ts deleted file mode 100644 index 939b5e5..0000000 --- a/backend/GeneratedCode/responses.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface SimpleResponse { - message: string; -} diff --git a/backend/GeneratedCode/systemUtils.ts b/backend/GeneratedCode/systemUtils.ts deleted file mode 100644 index 1be01e6..0000000 --- a/backend/GeneratedCode/systemUtils.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { api } from "./api"; -import type { Nullable } from "./apiTypes.ts"; - -// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 39 -export const health = async (): Promise<{ - data: string; - error: Nullable; -}> => { - return (await api.GET("/health")) as { - data: string; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/metrics; name=metrics; method=GET; response=string - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 42 -export const metrics = async (): Promise<{ - data: string; - error: Nullable; -}> => { - return (await api.GET("/metrics")) as { - data: string; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=[]MailDebugItem - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 54 -export const mailDebug = async (): Promise<{ - data: MailDebugItem[]; - error: Nullable; -}> => { - return (await api.GET("/maildebug")) as { - data: MailDebugItem[]; - error: Nullable; - }; -}; - -export interface MailDebugItem { - name: string; - content: string; -} diff --git a/backend/GeneratedCode/tokens.ts b/backend/GeneratedCode/tokens.ts deleted file mode 100644 index 769e217..0000000 --- a/backend/GeneratedCode/tokens.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface TokenPair { - access_token: string; - refresh_token: string; -} diff --git a/backend/GeneratedCode/users.ts b/backend/GeneratedCode/users.ts deleted file mode 100644 index af1e7d8..0000000 --- a/backend/GeneratedCode/users.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { api } from "./api"; -import type { Nullable } from "./apiTypes.ts"; -import type * as responses from "./responses.ts"; -import type * as tokens from "./tokens.ts"; - -// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=users.RefreshRequest; response=tokens.TokenPair - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 47 -export const refresh = async ( - data: RefreshRequest, -): Promise<{ data: tokens.TokenPair; error: Nullable }> => { - return (await api.POST("/auth/refresh", data)) as { - data: tokens.TokenPair; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=users.UpdateUserRequest; response=users.User - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 34 -export const updateUser = async ( - data: UpdateUserRequest, -): Promise<{ data: User; error: Nullable }> => { - return (await api.PUT("/users/:uuid", data)) as { - data: User; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=users.User - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 40 -export const me = async (): Promise<{ - data: User; - error: Nullable; -}> => { - return (await api.GET("/auth/me")) as { data: User; error: Nullable }; -}; - -// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=users.ForgotPasswordRequest; response=responses.SimpleResponse - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 53 -export const forgotPassword = async ( - data: ForgotPasswordRequest, -): Promise<{ data: responses.SimpleResponse; error: Nullable }> => { - return (await api.POST("/auth/password/forgot", data)) as { - data: responses.SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=users.ResetPasswordRequest; response=responses.SimpleResponse - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 56 -export const resetPassword = async ( - data: ResetPasswordRequest, -): Promise<{ data: responses.SimpleResponse; error: Nullable }> => { - return (await api.POST("/auth/password/reset", data)) as { - data: responses.SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=responses.SimpleResponse - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 59 -export const validToken = async ( - data: string, -): Promise<{ data: responses.SimpleResponse; error: Nullable }> => { - return (await api.POST("/auth/password/valid", data)) as { - data: responses.SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=responses.SimpleResponse - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 37 -export const deleteUser = async ( - uuid: string, -): Promise<{ data: responses.SimpleResponse; error: Nullable }> => { - return (await api.DELETE(`/users/${uuid}`)) as { - data: responses.SimpleResponse; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=users.User - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 28 -export const getUser = async ( - uuid: string, -): Promise<{ data: User; error: Nullable }> => { - return (await api.GET(`/users/${uuid}`)) as { - data: User; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=users.UserCreateRequest; response=users.User - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 50 -export const register = async ( - data: UserCreateRequest, -): Promise<{ data: User; error: Nullable }> => { - return (await api.POST("/auth/register", data)) as { - data: User; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateRequest; response=users.User - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 31 -export const createUser = async ( - data: UserCreateRequest, -): Promise<{ data: User; error: Nullable }> => { - return (await api.POST("/users", data)) as { - data: User; - error: Nullable; - }; -}; - -// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=users.LoginRequest; response=tokens.TokenPair - -// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 44 -export const login = async ( - data: LoginRequest, -): Promise<{ data: tokens.TokenPair; error: Nullable }> => { - return (await api.POST("/auth/login", data)) as { - data: tokens.TokenPair; - error: Nullable; - }; -}; - -export interface UpdateUserRequest { - name: string; - email: string; - password: string; - roles: UserRoles; - status: UserStatus; - types: UserTypes; - avatar: Nullable; - details: Nullable; - preferences: Nullable; -} - -export interface User { - id: number; - email: string; - name: string; - roles: UserRoles; - types: UserTypes; - status: UserStatus; - activatedAt: Date; - uuid: string; - details: Nullable; - preferences: Nullable; - avatar: Nullable; - createdAt: Date; - updatedAt: Date; -} - -export interface ForgotPasswordRequest { - email: string; -} - -export interface ResetPasswordRequest { - token: string; - password: string; -} - -export interface UserPreferences { - id: number; - userId: number; - useIdle: boolean; - idleTimeout: number; - useIdlePassword: boolean; - idlePin: string; - useDirectLogin: boolean; - useQuadcodeLogin: boolean; - sendNoticesMail: boolean; - language: string; - createdAt: Date; - updatedAt: Date; -} - -export interface UserCreateRequest { - name: string; - email: string; - password: string; - roles: UserRoles; - status: UserStatus; - types: UserTypes; - avatar: Nullable; - details: Nullable; - preferences: Nullable; -} - -export interface LoginRequest { - username: string; - password: string; -} - -export interface UserDetails { - id: number; - userId: number; - title: string; - firstName: string; - lastName: string; - address: string; - city: string; - zipCode: string; - country: string; - phone: string; - createdAt: Nullable; - updatedAt: Nullable; -} - -export interface RefreshRequest { - refresh_token: string; -} - -export type UserRoles = string[]; - -export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus]; - -export type UserTypes = string[]; - -export const EnumUserStatus = { - UserStatusPending: "pending", - UserStatusActive: "active", - UserStatusDisabled: "disabled", -} as const; diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index bd09ca4..bddcec3 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/signal" + "path/filepath" "strconv" "strings" "syscall" @@ -13,8 +14,8 @@ import ( "server/internal/config" "server/internal/db" + "server/internal/middleware" "server/internal/migrations" - "server/internal/roles" "server/internal/routes" "server/internal/tokens" @@ -30,6 +31,8 @@ import ( // Typescript: TSDeclaration= Nullable = T | null; // Typescript: TSDeclaration= Record = { [P in K]: T; } +const spaDistPath = "http/static/spa" + func main() { loadDotEnv(".env") @@ -97,9 +100,13 @@ func main() { return c.Next() }) - app.Use(roles.RequireEndpointPermission(dbConn, tokenService)) + api := app.Group("/api", middleware.GetAuthClaims(dbConn, tokenService)) - routes.Register(app) + routes.Register(api) + + app.Get("/", func(c fiber.Ctx) error { + return c.SendFile(filepath.Join(spaDistPath, "index.html")) + }) port := envOrDefault("PORT", "3000") diff --git a/backend/data/data.db b/backend/data/data.db index 8f896a6e10256c4a627429932f3ea1aa196533c5..ddfd2180285738dad2aacf9004564793ef56d1da 100644 GIT binary patch literal 81920 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

M0Q*V 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