Browse Source

chore: merge pull request #266

Northon Torga 2 months ago
parent
commit
a6727e36e6
100 changed files with 4852 additions and 4226 deletions
  1. 18 1
      CHANGELOG.md
  2. 2 2
      README.md
  3. 17 3
      container/nginx/user/index.html
  4. 12 4
      docs/AGENTS.md
  5. 4 2
      docs/DEVELOPMENT.md
  6. 12 11
      go.mod
  7. 39 40
      go.sum
  8. 4 4
      src/domain/dto/readDatabases.go
  9. 33 0
      src/domain/dto/runPhpCommand.go
  10. 3 0
      src/domain/dto/updateCron.go
  11. 3 0
      src/domain/dto/updateMapping.go
  12. 3 0
      src/domain/dto/updateMappingSecurityRule.go
  13. 1 0
      src/domain/repository/accountQueryRepo.go
  14. 5 3
      src/domain/repository/runtimeCmdRepo.go
  15. 13 4
      src/domain/useCase/deleteAccount.go
  16. 5 0
      src/domain/useCase/readAccounts.go
  17. 43 0
      src/domain/useCase/runPhpCommand.go
  18. 8 7
      src/domain/valueObject/accountId.go
  19. 26 0
      src/domain/valueObject/unixCommandOutput.go
  20. 20 0
      src/infra/account/accountQueryRepo.go
  21. 4 0
      src/infra/cron/cronCmdRepo.go
  22. 5 13
      src/infra/cron/cronCmdRepo_test.go
  23. 4 2
      src/infra/envs/envs.go
  24. 45 9
      src/infra/helper/runCmd.go
  25. 63 0
      src/infra/runtime/runtimeCmdRepo.go
  26. 2 2
      src/infra/runtime/runtimeQueryRepo.go
  27. 34 4
      src/infra/ssl/sslCmdRepo.go
  28. 15 0
      src/infra/vhost/mappingCmdRepo.go
  29. 1 2
      src/infra/webServer/webServerSetup.go
  30. 1 1
      src/presentation/api/api.go
  31. 15 15
      src/presentation/api/controller/account.go
  32. 5 5
      src/presentation/api/controller/authentication.go
  33. 10 10
      src/presentation/api/controller/cron.go
  34. 13 13
      src/presentation/api/controller/database.go
  35. 11 11
      src/presentation/api/controller/marketplace.go
  36. 4 4
      src/presentation/api/controller/o11y.go
  37. 38 17
      src/presentation/api/controller/runtime.go
  38. 7 7
      src/presentation/api/controller/scheduledTask.go
  39. 17 17
      src/presentation/api/controller/services.go
  40. 3 3
      src/presentation/api/controller/setup.go
  41. 11 11
      src/presentation/api/controller/ssl.go
  42. 27 27
      src/presentation/api/controller/virtualHost.go
  43. 68 1
      src/presentation/api/docs/docs.go
  44. 68 1
      src/presentation/api/docs/swagger.json
  45. 47 1
      src/presentation/api/docs/swagger.yaml
  46. 9 9
      src/presentation/api/helper/liaisonResponseWrapper.go
  47. 1 1
      src/presentation/api/helper/readRequestInputData.go
  48. 1 1
      src/presentation/api/middleware/authentication.go
  49. 17 0
      src/presentation/api/router.go
  50. 15 15
      src/presentation/cli/controller/account.go
  51. 5 5
      src/presentation/cli/controller/authentication.go
  52. 10 10
      src/presentation/cli/controller/cron.go
  53. 13 13
      src/presentation/cli/controller/database.go
  54. 11 11
      src/presentation/cli/controller/marketplace.go
  55. 4 4
      src/presentation/cli/controller/o11y.go
  56. 40 11
      src/presentation/cli/controller/runtime.go
  57. 7 7
      src/presentation/cli/controller/scheduledTask.go
  58. 15 15
      src/presentation/cli/controller/services.go
  59. 9 9
      src/presentation/cli/controller/ssl.go
  60. 27 27
      src/presentation/cli/controller/virtualHost.go
  61. 8 8
      src/presentation/cli/helper/liaisonResponseWrapper.go
  62. 1 0
      src/presentation/cli/router.go
  63. 419 0
      src/presentation/liaison/account.go
  64. 24 22
      src/presentation/liaison/authentication.go
  65. 291 0
      src/presentation/liaison/cron.go
  66. 352 0
      src/presentation/liaison/database.go
  67. 12 12
      src/presentation/liaison/helper/paginationParser.go
  68. 3 3
      src/presentation/liaison/helper/requiredParamsInspector.go
  69. 4 4
      src/presentation/liaison/helper/timeParamsParser.go
  70. 131 137
      src/presentation/liaison/marketplace.go
  71. 9 9
      src/presentation/liaison/o11y.go
  72. 6 6
      src/presentation/liaison/output.go
  73. 200 0
      src/presentation/liaison/runtime.go
  74. 59 59
      src/presentation/liaison/scheduledTask.go
  75. 937 0
      src/presentation/liaison/services.go
  76. 313 0
      src/presentation/liaison/ssl.go
  77. 1012 0
      src/presentation/liaison/virtualHost.go
  78. 0 415
      src/presentation/service/account.go
  79. 0 284
      src/presentation/service/cron.go
  80. 0 352
      src/presentation/service/database.go
  81. 0 134
      src/presentation/service/runtime.go
  82. 0 937
      src/presentation/service/services.go
  83. 0 313
      src/presentation/service/ssl.go
  84. 0 994
      src/presentation/service/virtualHost.go
  85. 0 72
      src/presentation/ui/assets/additional.js
  86. 5 2
      src/presentation/ui/component/form/dropzone.templ
  87. 2 4
      src/presentation/ui/component/form/dropzoneState.js
  88. 2 4
      src/presentation/ui/component/form/fileUploadTextInputFileContentReader.templ
  89. 2 4
      src/presentation/ui/component/form/multiColumnRepeatableFieldset.templ
  90. 2 4
      src/presentation/ui/component/form/multiSelectInputState.js
  91. 3 2
      src/presentation/ui/component/form/passwordInput.templ
  92. 4 6
      src/presentation/ui/component/form/passwordInputState.js
  93. 5 2
      src/presentation/ui/component/form/selectInput.templ
  94. 3 2
      src/presentation/ui/component/structural/deleteModal.templ
  95. 49 23
      src/presentation/ui/layout/login/login.templ
  96. 11 8
      src/presentation/ui/layout/login/state.js
  97. 2 1
      src/presentation/ui/layout/main/main.templ
  98. 2 1
      src/presentation/ui/layout/setup/setup.templ
  99. 4 6
      src/presentation/ui/layout/setup/state.js
  100. 2 1
      src/presentation/ui/presenter/accounts/index.templ

+ 18 - 1
CHANGELOG.md

@@ -1,7 +1,24 @@
 # Changelog
 
 ```log
-0.2.5 - 2025/06/27
+0.2.6 - 2025/06/06
+feat: runtime php run
+feat: implement clearable fields for cron and mappings
+feat: add dash link to default index
+feat: prevent only account deletion
+fix: add debug logs to ssl watchdog
+fix(api): rename accountId to operatorAccountId
+feat(ui): safe prefill user and pass on login via query params
+fix(ui): missing capital letters on modals
+fix(ui): use UiToolset instead of local utilities
+fix(ui): add loading until login finishes redirect
+fix(ui): add file.name to file manager update file content
+fix(ui): resize code editor when modal is resized
+fix(ui): keep file line in active state when selected
+fix(ui): code editor height when full screen
+fix(ui): php update settings missing vhost
+
+0.2.5 - 2025/05/27
 refactor(ui): sidebar
 refactor(ui): merge page and presenters
 refactor(ui): move state.js to individual embeds

+ 2 - 2
README.md

@@ -1,4 +1,4 @@
-# [Infinite OS](https://goinfinite.net/os/) · [![Roadmap](https://img.shields.io/badge/roadmap-014737)](https://github.com/orgs/goinfinite/projects/9) [![Demo](https://img.shields.io/badge/read--only_demo-233876)](https://os.demo.goinfinite.net:1618/) [![/r/goinfinite](https://img.shields.io/badge/%2Fr%2Fgoinfinite-FF4500?logo=reddit&logoColor=ffffff)](https://www.reddit.com/r/goinfinite/) [![Discussions](https://img.shields.io/badge/discussions-751A3D?logo=github)](https://github.com/orgs/goinfinite/discussions) [![Report Card](https://img.shields.io/badge/report-A%2B-brightgreen)](https://goreportcard.com/report/github.com/goinfinite/os) [![License](https://img.shields.io/badge/license-EPL-blue.svg)](https://github.com/goinfinite/os/blob/main/LICENSE.md)
+# [Infinite OS](https://goinfinite.net/os/) · [![Roadmap](https://img.shields.io/badge/roadmap-014737)](https://github.com/orgs/goinfinite/projects/9) [![Demo](https://img.shields.io/badge/read--only_demo-233876)](https://os.demo.goinfinite.net:1618/?prefilledUsername=demo&prefilledPassword=abc123) [![/r/goinfinite](https://img.shields.io/badge/%2Fr%2Fgoinfinite-FF4500?logo=reddit&logoColor=ffffff)](https://www.reddit.com/r/goinfinite/) [![Discussions](https://img.shields.io/badge/discussions-751A3D?logo=github)](https://github.com/orgs/goinfinite/discussions) [![Report Card](https://img.shields.io/badge/report-A%2B-brightgreen)](https://goreportcard.com/report/github.com/goinfinite/os) [![License](https://img.shields.io/badge/license-EPL-blue.svg)](https://github.com/goinfinite/os/blob/main/LICENSE.md)
 
 Infinite OS is the simplest way to deploy containerized applications. Full stop. Even if containers sound like rocket science to you, you'll be launching apps with just a few clicks. 🚀
 
@@ -35,7 +35,7 @@ Though if you fancy an even smoother experience, do check out our sibling projec
 
 ## Online Demo
 
-A read-only demo of the dashboard is available at [https://os.demo.goinfinite.net:1618/](https://os.demo.goinfinite.net:1618/). The default credentials are `demo` (user) and `abc123` (password). You can use this demo to explore the dashboard and see how it works. Please note that this is a read-only demo, so you won't be able to make any changes or deploy any applications.
+A read-only demo of the dashboard is available at [https://os.demo.goinfinite.net:1618/?prefilledUsername=demo&prefilledPassword=abc123](https://os.demo.goinfinite.net:1618/). The default credentials are `demo` (user) and `abc123` (password). You can use this demo to explore the dashboard and see how it works. Please note that this is a read-only demo, so you won't be able to make any changes or deploy any applications.
 
 ## Features
 

+ 17 - 3
container/nginx/user/index.html

@@ -5,8 +5,7 @@
   <title>Welcome to Infinite OS!</title>
   <link rel="preconnect" href="https://fonts.googleapis.com" />
   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
-  <link
-    href="https://fonts.googleapis.com/css?family=Lato:regular,italic,700italic,|Space+Grotesk:700,regular,italic,700italic,&display=swap"
+  <link href="https://fonts.googleapis.com/css?family=Lato:700,regular,italic,700italic&display=swap"
     rel="stylesheet" />
   <style>
     html {
@@ -22,7 +21,7 @@
     }
 
     h1 {
-      font-family: 'Space Grotesk', Arial, sans-serif;
+      font-family: 'Times New Roman', 'Lucida Grande', Helvetica, Arial, sans-serif;
     }
   </style>
 </head>
@@ -32,6 +31,12 @@
   <p>If you see this page, Infinite OS was successfully deployed and is
     working.</p>
 
+  <p>
+    <strong>
+      To access the dashboard, visit <a id="dashboard-link" href="https://localhost:1618">https://localhost:1618</a>.
+    </strong>
+  </p>
+
   <p>
     For documentation and community support, please refer to the project's <a
       href="https://github.com/goinfinite/os">GitHub repository</a>.
@@ -43,4 +48,13 @@
   <p><em>Thank you for using Infinite OS.</em></p>
 </body>
 
+<script type="text/javascript">
+  const dashboardLinkUrl = `https://${window.location.hostname}:1618`;
+  const dashboardLinkElement = document.getElementById('dashboard-link');
+  if (dashboardLinkElement) {
+    dashboardLinkElement.href = dashboardLinkUrl;
+    dashboardLinkElement.textContent = dashboardLinkUrl;
+  }
+</script>
+
 </html>

+ 12 - 4
docs/AGENTS.md

@@ -1,19 +1,27 @@
-# General Rules
+# Project Specific Rules
 
-# @source https://github.com/goinfinite/tk/blob/main/docs/AGENTS.md
+- Unit tests CANNOT be executed on your local machine. They MUST be executed using a container built from the Containerfile.test.
+
+# Infinite Standard Agent Guidelines
+
+## @source https://github.com/goinfinite/tk/blob/main/docs/AGENTS.md
+
+## General Rules
 
 - NEVER create new features that weren't explicitly requested, even if they seem like useful additions.
 - Ambiguity SHOULD always be questioned as it generates doubts about the intention of the developer.
 - Value objects, infrastructure and use cases (with complex logic) MUST have unit tests.
 - Unit tests SHOULD use testCases as much as possible.
+- During delete operations, attempt to validate all constraints upfront rather than discovering them mid-operation.
 
-# Code Style Rules
+## Code Style Rules
 
 - Avoid comments unless strictly necessary.
 - Method names should focus on the conceptual purpose rather than implementation details.
 - NEVER use 'else' statements unless it's the UI layer.
 - NEVER use single letter variable names. Use descriptive names, but avoid long names.
 - When naming variables, try to choose names that convey the intention or purpose rather than just describing what the variable stores.
+- Variable names should reflect the primary flow, not conditional outcomes.
 - Use purposeful named return values whenever the method returns multiple values.
 - Use Ptr suffix on variables when parsing optional fields (usually pointers on DTOs).
 - Prefer value objects as custom primitive types rather than structs when possible.
@@ -26,7 +34,7 @@
 - Struct fields should be ordered by importance, followed by alphabetical order.
 - Struct required fields should be placed before optional (pointer) fields.
 
-# Go(lang) Specific Rules
+## Go(lang) Specific Rules
 
 - Prefer using slog.Error or slog.Debug instead of log.Printf depending on the gravity of the log.
 - Value objects accept interface{}/any directly without the need for pre-assertion.

+ 4 - 2
docs/DEVELOPMENT.md

@@ -67,14 +67,16 @@ sudo chown $(whoami):$(whoami) /infinite
 
 ## Web UIs
 
-This project web UI is based on a combo [Templ](https://templ.guide/) + [Alpine.js](https://alpinejs.dev/) + [HTMX](https://htmx.org/docs/).
+This project web UI is based on a combo [Templ](https://templ.guide/) + [Alpine.js](https://alpinejs.dev/) + [HTMX](https://htmx.org/docs/) + [Tailwind CSS](https://tailwindcss.com/).
 
 To understand the entire conceptual and theoretical foundation behind using these technologies to create a new frontend architecture, [access this article](https://ntorga.com/full-stack-go-app-with-htmx-and-alpinejs/). However, to grasp the practical basis of how to apply this new architecture, [refer to the proof of concept](https://github.com/ntorga/clean-ddd-full-stack-go-poc) used to develop it.
 
+The UI uses [goinfinite/ui](https://github.com/goinfinite/ui) library to provide most of the common components and utilities.
+
 For the interface code to be read and rendered by Go, we need to convert all `.templ` files into `.go` files. To do this, run the following command at the root of the application:
 
 ```
-templ generate -path src/presentation/api
+templ generate -path src/presentation/ui
 ```
 
 It is important that this is done before using Air to create the binary; otherwise, the Web UI will not be embedded, and you will not be able to use it.

+ 12 - 11
go.mod

@@ -3,11 +3,11 @@ module github.com/goinfinite/os
 go 1.24.3
 
 require (
-	github.com/a-h/templ v0.3.865
+	github.com/a-h/templ v0.3.898
 	github.com/alecthomas/chroma v0.10.0
 	github.com/glebarez/sqlite v1.11.0
-	github.com/goinfinite/tk v0.0.2
-	github.com/goinfinite/ui v0.0.8
+	github.com/goinfinite/tk v0.0.5
+	github.com/goinfinite/ui v0.1.5
 	github.com/golang-jwt/jwt v3.2.2+incompatible
 	github.com/google/uuid v1.6.0
 	github.com/iancoleman/strcase v0.3.0
@@ -19,10 +19,10 @@ require (
 	github.com/spf13/cobra v1.9.1
 	github.com/swaggo/echo-swagger v1.4.1
 	github.com/swaggo/swag v1.16.4
-	golang.org/x/crypto v0.38.0
-	golang.org/x/net v0.40.0
+	golang.org/x/crypto v0.39.0
+	golang.org/x/net v0.41.0
 	golang.org/x/term v0.32.0
-	golang.org/x/text v0.25.0
+	golang.org/x/text v0.26.0
 	gorm.io/gorm v1.30.0
 )
 
@@ -30,6 +30,7 @@ require (
 	github.com/KyleBanks/depth v1.2.1 // indirect
 	github.com/dlclark/regexp2 v1.11.5 // indirect
 	github.com/dustin/go-humanize v1.0.1
+	github.com/evanw/esbuild v0.25.5 // indirect
 	github.com/ghodss/yaml v1.0.0 // indirect
 	github.com/glebarez/go-sqlite v1.22.0 // indirect
 	github.com/go-ole/go-ole v1.3.0 // indirect
@@ -37,7 +38,7 @@ require (
 	github.com/go-openapi/jsonreference v0.21.0 // indirect
 	github.com/go-openapi/spec v0.21.0 // indirect
 	github.com/go-openapi/swag v0.23.1 // indirect
-	github.com/goccy/go-yaml v1.17.1 // indirect
+	github.com/goccy/go-yaml v1.18.0 // indirect
 	github.com/google/go-cmp v0.7.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
@@ -59,13 +60,13 @@ require (
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/valyala/fasttemplate v1.2.2 // indirect
 	github.com/yusufpapurcu/wmi v1.2.4 // indirect
-	golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
+	golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
 	golang.org/x/sys v0.33.0 // indirect
-	golang.org/x/time v0.11.0 // indirect
-	golang.org/x/tools v0.33.0 // indirect
+	golang.org/x/time v0.12.0 // indirect
+	golang.org/x/tools v0.34.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	modernc.org/libc v1.65.8 // indirect
+	modernc.org/libc v1.65.10 // indirect
 	modernc.org/mathutil v1.7.1 // indirect
 	modernc.org/memory v1.11.0 // indirect
 	modernc.org/sqlite v1.37.1 // indirect

+ 39 - 40
go.sum

@@ -1,7 +1,9 @@
 github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
 github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
-github.com/a-h/templ v0.3.865 h1:nYn5EWm9EiXaDgWcMQaKiKvrydqgxDUtT1+4zU2C43A=
-github.com/a-h/templ v0.3.865/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ=
+github.com/a-h/templ v0.3.894 h1:umG2i6ypJXtm9CrOZxwlYBXgDnpcUnMl6IaHpXGXiGE=
+github.com/a-h/templ v0.3.894/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ=
+github.com/a-h/templ v0.3.898 h1:g9oxL/dmM6tvwRe2egJS8hBDQTncokbMoOFk1oJMX7s=
+github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ=
 github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
 github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -14,6 +16,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
 github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/evanw/esbuild v0.25.5 h1:E+JpeY5S/1LFmnX1vtuZqUKT7qDVcfXdhzMhM3uIKFs=
+github.com/evanw/esbuild v0.25.5/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
@@ -31,25 +35,17 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
 github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
 github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
 github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
-github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
-github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
+github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
+github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/goinfinite/tk v0.0.2 h1:4dPVvRGrnDMRBTBxkh+u/T4GX4ntYRbbUns21k8quRc=
-github.com/goinfinite/tk v0.0.2/go.mod h1:dRo2mAiJUbLTJTtAbKSvtyUYs2htgXCcsQyGAahOUqQ=
-github.com/goinfinite/ui v0.0.2 h1:TKMwOuVQ4QbyRx3xtUSIT/n/FJdC6eN1+CYOnDkI9n4=
-github.com/goinfinite/ui v0.0.2/go.mod h1:6RqsvJOKf5ATOAqhsBXgzLWHR1YSH2VonZk8YQnmi6E=
-github.com/goinfinite/ui v0.0.3 h1:CPwhkI8g/soVydYwZSwYcyfrEkknv5JQKeElC8wiWUE=
-github.com/goinfinite/ui v0.0.3/go.mod h1:6RqsvJOKf5ATOAqhsBXgzLWHR1YSH2VonZk8YQnmi6E=
-github.com/goinfinite/ui v0.0.4 h1:X0g90o/9jhHUN6XSHrVM9SDbzyLV5PgG1ivf3M33lvI=
-github.com/goinfinite/ui v0.0.4/go.mod h1:6RqsvJOKf5ATOAqhsBXgzLWHR1YSH2VonZk8YQnmi6E=
-github.com/goinfinite/ui v0.0.5 h1:g4uYFbsqKuX+7hRluGHph+Yp2KfId/36V7wA0UhCgvc=
-github.com/goinfinite/ui v0.0.5/go.mod h1:6RqsvJOKf5ATOAqhsBXgzLWHR1YSH2VonZk8YQnmi6E=
-github.com/goinfinite/ui v0.0.6 h1:G5yr+AYNJquGc0D4dHkXkPLeFGzzPaxrQ1ez4w9fH+A=
-github.com/goinfinite/ui v0.0.6/go.mod h1:6RqsvJOKf5ATOAqhsBXgzLWHR1YSH2VonZk8YQnmi6E=
-github.com/goinfinite/ui v0.0.7 h1:j1tn5ZOXgWZWpaNnQWyOKd49HWAgkehhg66rjxK5Xi0=
-github.com/goinfinite/ui v0.0.7/go.mod h1:6RqsvJOKf5ATOAqhsBXgzLWHR1YSH2VonZk8YQnmi6E=
-github.com/goinfinite/ui v0.0.8 h1:fhZe4vZ5l18WUd5IK5/n+vjzCuLfKhRjXffKVNfqH1s=
-github.com/goinfinite/ui v0.0.8/go.mod h1:6RqsvJOKf5ATOAqhsBXgzLWHR1YSH2VonZk8YQnmi6E=
+github.com/goinfinite/tk v0.0.5 h1:1jFxiq0kdGSoQez3iTqXa/gIOzaQtsxtZac1HzWbR5Q=
+github.com/goinfinite/tk v0.0.5/go.mod h1:IxDoQsqvCs8OKFmBMdgz8TtY9sm+6/M1cnHZYXlE2Hw=
+github.com/goinfinite/ui v0.1.3 h1:BpLOtdAsWR/fC6Cb0k8LouAVdBUfPoUWJI9H8gJGf8Q=
+github.com/goinfinite/ui v0.1.3/go.mod h1:sYK7OSVdph8YtC8ZExyYXdbDzjGYgY7wKA04t8KQnbo=
+github.com/goinfinite/ui v0.1.4 h1:PiRLx4c8z+1j8byfEI1wVg4lgYlKt5O0GHfUP0V3Ch8=
+github.com/goinfinite/ui v0.1.4/go.mod h1:sYK7OSVdph8YtC8ZExyYXdbDzjGYgY7wKA04t8KQnbo=
+github.com/goinfinite/ui v0.1.5 h1:6d6eQEl4iUEtbmJMtvSHobWUh3E5sdV0RnioPg3kqUg=
+github.com/goinfinite/ui v0.1.5/go.mod h1:is0sTHvtA/VolUJFsgDHH0vv1YapWKlQELU8Nj5GmNE=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -132,17 +128,20 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
 github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
 github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
-golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
-golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
-golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
-golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
-golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
-golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
-golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
-golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
-golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
-golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
+golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
+golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
+golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4=
+golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
+golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
+golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
+golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
+golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
+golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -151,12 +150,14 @@ golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
 golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
 golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
-golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
-golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
-golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
-golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
+golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
+golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
+golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
 golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
 golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
+golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
+golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -165,20 +166,18 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
-gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
 gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
 gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
 modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
 modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
 modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
 modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
-modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
-modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
+modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=
+modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
 modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
 modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
-modernc.org/libc v1.65.8 h1:7PXRJai0TXZ8uNA3srsmYzmTyrLoHImV5QxHeni108Q=
-modernc.org/libc v1.65.8/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
+modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc=
+modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po=
 modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
 modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
 modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=

+ 4 - 4
src/domain/dto/readDatabases.go

@@ -6,10 +6,10 @@ import (
 )
 
 type ReadDatabasesRequest struct {
-	Pagination   Pagination                  `json:"pagination"`
-	DatabaseName *valueObject.DatabaseName   `json:"name,omitempty"`
-	DatabaseType *valueObject.DatabaseType   `json:"type,omitempty"`
-	Username     *valueObject.DatabaseUsername `json:"username,omitempty"`
+	Pagination   Pagination                    `json:"pagination"`
+	DatabaseName *valueObject.DatabaseName     `json:"name"`
+	DatabaseType *valueObject.DatabaseType     `json:"type"`
+	Username     *valueObject.DatabaseUsername `json:"username"`
 }
 
 type ReadDatabasesResponse struct {

+ 33 - 0
src/domain/dto/runPhpCommand.go

@@ -0,0 +1,33 @@
+package dto
+
+import "github.com/goinfinite/os/src/domain/valueObject"
+
+type RunPhpCommandRequest struct {
+	Hostname          valueObject.Fqdn        `json:"hostname"`
+	Command           valueObject.UnixCommand `json:"command"`
+	TimeoutSecs       *uint64                 `json:"timeoutSecs"`
+	OperatorAccountId valueObject.AccountId   `json:"-"`
+	OperatorIpAddress valueObject.IpAddress   `json:"-"`
+}
+
+func NewRunPhpCommandRequest(
+	hostname valueObject.Fqdn,
+	command valueObject.UnixCommand,
+	timeoutSecs *uint64,
+	operatorAccountId valueObject.AccountId,
+	operatorIpAddress valueObject.IpAddress,
+) RunPhpCommandRequest {
+	return RunPhpCommandRequest{
+		Hostname:          hostname,
+		Command:           command,
+		TimeoutSecs:       timeoutSecs,
+		OperatorAccountId: operatorAccountId,
+		OperatorIpAddress: operatorIpAddress,
+	}
+}
+
+type RunPhpCommandResponse struct {
+	StdOutput *valueObject.UnixCommandOutput `json:"stdOut"`
+	StdError  *valueObject.UnixCommandOutput `json:"stdErr"`
+	ExitCode  *int                           `json:"exitCode"`
+}

+ 3 - 0
src/domain/dto/updateCron.go

@@ -7,6 +7,7 @@ type UpdateCron struct {
 	Schedule          *valueObject.CronSchedule `json:"schedule"`
 	Command           *valueObject.UnixCommand  `json:"command"`
 	Comment           *valueObject.CronComment  `json:"comment"`
+	ClearableFields   []string                  `json:"-"`
 	OperatorAccountId valueObject.AccountId     `json:"-"`
 	OperatorIpAddress valueObject.IpAddress     `json:"-"`
 }
@@ -16,6 +17,7 @@ func NewUpdateCron(
 	schedule *valueObject.CronSchedule,
 	command *valueObject.UnixCommand,
 	comment *valueObject.CronComment,
+	clearableFields []string,
 	operatorAccountId valueObject.AccountId,
 	operatorIpAddress valueObject.IpAddress,
 ) UpdateCron {
@@ -24,6 +26,7 @@ func NewUpdateCron(
 		Schedule:          schedule,
 		Command:           command,
 		Comment:           comment,
+		ClearableFields:   clearableFields,
 		OperatorAccountId: operatorAccountId,
 		OperatorIpAddress: operatorIpAddress,
 	}

+ 3 - 0
src/domain/dto/updateMapping.go

@@ -11,6 +11,7 @@ type UpdateMapping struct {
 	TargetHttpResponseCode        *valueObject.HttpResponseCode      `json:"targetHttpResponseCode"`
 	ShouldUpgradeInsecureRequests *bool                              `json:"shouldUpgradeInsecureRequests"`
 	MappingSecurityRuleId         *valueObject.MappingSecurityRuleId `json:"mappingSecurityRuleId"`
+	ClearableFields               []string                           `json:"-"`
 	OperatorAccountId             valueObject.AccountId              `json:"-"`
 	OperatorIpAddress             valueObject.IpAddress              `json:"-"`
 }
@@ -24,6 +25,7 @@ func NewUpdateMapping(
 	targetHttpResponseCode *valueObject.HttpResponseCode,
 	shouldUpgradeInsecureRequests *bool,
 	mappingSecurityRuleId *valueObject.MappingSecurityRuleId,
+	clearableFields []string,
 	operatorAccountId valueObject.AccountId,
 	operatorIpAddress valueObject.IpAddress,
 ) UpdateMapping {
@@ -36,6 +38,7 @@ func NewUpdateMapping(
 		TargetHttpResponseCode:        targetHttpResponseCode,
 		ShouldUpgradeInsecureRequests: shouldUpgradeInsecureRequests,
 		MappingSecurityRuleId:         mappingSecurityRuleId,
+		ClearableFields:               clearableFields,
 		OperatorAccountId:             operatorAccountId,
 		OperatorIpAddress:             operatorIpAddress,
 	}

+ 3 - 0
src/domain/dto/updateMappingSecurityRule.go

@@ -18,6 +18,7 @@ type UpdateMappingSecurityRule struct {
 	BandwidthBpsLimitPerConnection *valueObject.Byte                           `json:"bandwidthBpsLimitPerConnection"`
 	BandwidthLimitOnlyAfterBytes   *valueObject.Byte                           `json:"bandwidthLimitOnlyAfterBytes"`
 	ResponseCodeOnMaxConnections   *uint                                       `json:"responseCodeOnMaxConnections"`
+	ClearableFields                []string                                    `json:"-"`
 	OperatorAccountId              valueObject.AccountId                       `json:"-"`
 	OperatorIpAddress              valueObject.IpAddress                       `json:"-"`
 }
@@ -30,6 +31,7 @@ func NewUpdateMappingSecurityRule(
 	rpsSoftLimitPerIp, rpsHardLimitPerIp, responseCodeOnMaxRequests, maxConnectionsPerIp *uint,
 	bandwidthBpsLimitPerConnection, bandwidthLimitOnlyAfterBytes *valueObject.Byte,
 	responseCodeOnMaxConnections *uint,
+	clearableFields []string,
 	operatorAccountId valueObject.AccountId,
 	operatorIpAddress valueObject.IpAddress,
 ) UpdateMappingSecurityRule {
@@ -46,6 +48,7 @@ func NewUpdateMappingSecurityRule(
 		BandwidthBpsLimitPerConnection: bandwidthBpsLimitPerConnection,
 		BandwidthLimitOnlyAfterBytes:   bandwidthLimitOnlyAfterBytes,
 		ResponseCodeOnMaxConnections:   responseCodeOnMaxConnections,
+		ClearableFields:                clearableFields,
 		OperatorAccountId:              operatorAccountId,
 		OperatorIpAddress:              operatorIpAddress,
 	}

+ 1 - 0
src/domain/repository/accountQueryRepo.go

@@ -6,6 +6,7 @@ import (
 )
 
 type AccountQueryRepo interface {
+	Count(dto.ReadAccountsRequest) (uint64, error)
 	Read(dto.ReadAccountsRequest) (dto.ReadAccountsResponse, error)
 	ReadFirst(dto.ReadAccountsRequest) (entity.Account, error)
 	ReadSecureAccessPublicKeys(

+ 5 - 3
src/domain/repository/runtimeCmdRepo.go

@@ -1,12 +1,14 @@
 package repository
 
 import (
+	"github.com/goinfinite/os/src/domain/dto"
 	"github.com/goinfinite/os/src/domain/entity"
 	"github.com/goinfinite/os/src/domain/valueObject"
 )
 
 type RuntimeCmdRepo interface {
-	UpdatePhpVersion(hostname valueObject.Fqdn, version valueObject.PhpVersion) error
-	UpdatePhpSettings(hostname valueObject.Fqdn, settings []entity.PhpSetting) error
-	UpdatePhpModules(hostname valueObject.Fqdn, modules []entity.PhpModule) error
+	RunPhpCommand(dto.RunPhpCommandRequest) (dto.RunPhpCommandResponse, error)
+	UpdatePhpVersion(valueObject.Fqdn, valueObject.PhpVersion) error
+	UpdatePhpSettings(valueObject.Fqdn, []entity.PhpSetting) error
+	UpdatePhpModules(valueObject.Fqdn, []entity.PhpModule) error
 }

+ 13 - 4
src/domain/useCase/deleteAccount.go

@@ -14,14 +14,23 @@ func DeleteAccount(
 	activityRecordCmdRepo repository.ActivityRecordCmdRepo,
 	deleteDto dto.DeleteAccount,
 ) error {
-	readRequestDto := dto.ReadAccountsRequest{
-		AccountId: &deleteDto.AccountId,
-	}
-	_, err := accountQueryRepo.ReadFirst(readRequestDto)
+	_, err := accountQueryRepo.ReadFirst(
+		dto.ReadAccountsRequest{AccountId: &deleteDto.AccountId},
+	)
 	if err != nil {
 		return errors.New("AccountNotFound")
 	}
 
+	accountsCount, err := accountQueryRepo.Count(dto.ReadAccountsRequest{})
+	if err != nil {
+		slog.Error("CountAccountsError", slog.String("err", err.Error()))
+		return errors.New("CountAccountsInfraError")
+	}
+
+	if accountsCount <= 1 {
+		return errors.New("AtLeastOneAccountMustExist")
+	}
+
 	err = accountCmdRepo.Delete(deleteDto.AccountId)
 	if err != nil {
 		slog.Error("DeleteAccountError", slog.String("err", err.Error()))

+ 5 - 0
src/domain/useCase/readAccounts.go

@@ -8,6 +8,11 @@ import (
 	"github.com/goinfinite/os/src/domain/repository"
 )
 
+var AccountsDefaultPagination dto.Pagination = dto.Pagination{
+	PageNumber:   0,
+	ItemsPerPage: 10,
+}
+
 func ReadAccounts(
 	accountQueryRepo repository.AccountQueryRepo,
 	requestDto dto.ReadAccountsRequest,

+ 43 - 0
src/domain/useCase/runPhpCommand.go

@@ -0,0 +1,43 @@
+package useCase
+
+import (
+	"errors"
+	"log/slog"
+
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/repository"
+	"github.com/goinfinite/os/src/domain/valueObject"
+)
+
+var RuntimeRunPhpCommandTimeoutSecsDefault uint64 = 600
+
+func RunPhpCommand(
+	accountQueryRepo repository.AccountQueryRepo,
+	runtimeCmdRepo repository.RuntimeCmdRepo,
+	runRequest dto.RunPhpCommandRequest,
+) (runResponse dto.RunPhpCommandResponse, err error) {
+	if runRequest.OperatorAccountId != valueObject.AccountIdSystem {
+		operatorAccountEntity, err := accountQueryRepo.ReadFirst(
+			dto.ReadAccountsRequest{AccountId: &runRequest.OperatorAccountId},
+		)
+		if err != nil {
+			return runResponse, err
+		}
+
+		if !operatorAccountEntity.IsSuperAdmin {
+			return runResponse, errors.New("OperatorIsNotSuperAdmin")
+		}
+	}
+
+	if runRequest.TimeoutSecs == nil {
+		runRequest.TimeoutSecs = &RuntimeRunPhpCommandTimeoutSecsDefault
+	}
+
+	runResponse, err = runtimeCmdRepo.RunPhpCommand(runRequest)
+	if err != nil {
+		slog.Error("RunPhpCommandError", slog.String("err", err.Error()))
+		return runResponse, err
+	}
+
+	return runResponse, nil
+}

+ 8 - 7
src/domain/valueObject/accountId.go

@@ -4,20 +4,21 @@ import (
 	"errors"
 	"strconv"
 
-	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
+	tkVoUtil "github.com/goinfinite/tk/src/domain/valueObject/util"
 )
 
-type AccountId uint64
-
 var AccountIdSystem = AccountId(0)
+var AccountIdNobody = AccountId(65534)
+
+type AccountId uint64
 
-func NewAccountId(value interface{}) (accountId AccountId, err error) {
-	uintValue, err := voHelper.InterfaceToUint(value)
+func NewAccountId(rawValue any) (accountId AccountId, err error) {
+	uint64Value, err := tkVoUtil.InterfaceToUint64(rawValue)
 	if err != nil {
-		return accountId, errors.New("AccountIdMustBeUint")
+		return accountId, errors.New("AccountIdMustBeUint64")
 	}
 
-	return AccountId(uintValue), nil
+	return AccountId(uint64Value), nil
 }
 
 func (vo AccountId) Uint64() uint64 {

+ 26 - 0
src/domain/valueObject/unixCommandOutput.go

@@ -0,0 +1,26 @@
+package valueObject
+
+import (
+	"errors"
+
+	tkVoUtil "github.com/goinfinite/tk/src/domain/valueObject/util"
+)
+
+type UnixCommandOutput string
+
+func NewUnixCommandOutput(rawValue any) (cmdOutput UnixCommandOutput, err error) {
+	stringValue, err := tkVoUtil.InterfaceToString(rawValue)
+	if err != nil {
+		return cmdOutput, errors.New("UnixCommandOutputMustBeString")
+	}
+
+	if len(stringValue) > 4096 {
+		stringValue = stringValue[:4096]
+	}
+
+	return UnixCommandOutput(stringValue), nil
+}
+
+func (vo UnixCommandOutput) String() string {
+	return string(vo)
+}

+ 20 - 0
src/infra/account/accountQueryRepo.go

@@ -24,6 +24,26 @@ func NewAccountQueryRepo(
 	}
 }
 
+func (repo *AccountQueryRepo) Count(
+	requestDto dto.ReadAccountsRequest,
+) (count uint64, err error) {
+	model := dbModel.Account{}
+	if requestDto.AccountId != nil {
+		model.ID = requestDto.AccountId.Uint64()
+	}
+	if requestDto.AccountUsername != nil {
+		model.Username = requestDto.AccountUsername.String()
+	}
+
+	var itemsTotal int64
+	err = repo.persistentDbSvc.Handler.Model(&model).Where(&model).Count(&itemsTotal).Error
+	if err != nil {
+		return count, errors.New("CountAccountsTotalError: " + err.Error())
+	}
+
+	return uint64(itemsTotal), nil
+}
+
 func (repo *AccountQueryRepo) Read(
 	requestDto dto.ReadAccountsRequest,
 ) (responseDto dto.ReadAccountsResponse, err error) {

+ 4 - 0
src/infra/cron/cronCmdRepo.go

@@ -3,6 +3,7 @@ package cronInfra
 import (
 	"errors"
 	"os"
+	"slices"
 
 	"github.com/goinfinite/os/src/domain/dto"
 	"github.com/goinfinite/os/src/domain/entity"
@@ -114,6 +115,9 @@ func (repo *CronCmdRepo) Update(updateDto dto.UpdateCron) error {
 	if updateDto.Comment != nil {
 		comment = updateDto.Comment
 	}
+	if slices.Contains(updateDto.ClearableFields, "comment") {
+		comment = nil
+	}
 
 	desiredCronWithUpdatedValues := entity.NewCron(
 		desiredCron.Id, schedule, command, comment,

+ 5 - 13
src/infra/cron/cronCmdRepo_test.go

@@ -16,7 +16,7 @@ func TestCronCmdRepo(t *testing.T) {
 	schedule, _ := valueObject.NewCronSchedule("* * * * *")
 	command, _ := valueObject.NewUnixCommand("echo \"cronTest\" >> crontab_log.txt")
 	comment, _ := valueObject.NewCronComment("Test cron job")
-	operatorAccountId, _ := valueObject.NewAccountId(0)
+	operatorAccountId := valueObject.AccountIdSystem
 	operatorIpAddress := valueObject.IpAddressSystem
 
 	createCron := dto.NewCreateCron(
@@ -27,34 +27,26 @@ func TestCronCmdRepo(t *testing.T) {
 		var err error
 		id, err = cronCmdRepo.Create(createCron)
 		if err != nil {
-			t.Fatalf(
-				"Expected no error for '%s', but got '%s'", comment.String(),
-				err.Error(),
-			)
+			t.Fatalf("ExpectedNoErrorButGot: '%s'", err.Error())
 		}
 	})
 
 	t.Run("UpdateCron", func(t *testing.T) {
 		schedule, _ = valueObject.NewCronSchedule("* * * * 0")
 		updateCron := dto.NewUpdateCron(
-			id, &schedule, nil, nil, operatorAccountId, operatorIpAddress,
+			id, &schedule, nil, nil, []string{}, operatorAccountId, operatorIpAddress,
 		)
 
 		err := cronCmdRepo.Update(updateCron)
 		if err != nil {
-			t.Errorf(
-				"Expected no error for '%s', but got '%s'", comment.String(),
-				err.Error(),
-			)
+			t.Errorf("ExpectedNoErrorButGot: '%s'", err.Error())
 		}
 	})
 
 	t.Run("DeleteCron", func(t *testing.T) {
 		err := cronCmdRepo.Delete(id)
 		if err != nil {
-			t.Errorf(
-				"Expected no error for '%d', but got '%s'", id.Uint64(), err.Error(),
-			)
+			t.Errorf("ExpectedNoErrorButGot: '%s'", err.Error())
 		}
 	})
 }

+ 4 - 2
src/infra/envs/envs.go

@@ -1,7 +1,7 @@
 package infraEnvs
 
 const (
-	InfiniteOsVersion                  string = "0.2.5"
+	InfiniteOsVersion                  string = "0.2.6"
 	InfiniteOsMainDir                  string = "/infinite"
 	InfiniteOsBinary                   string = InfiniteOsMainDir + "/os"
 	InfiniteOsEnvFilePath              string = InfiniteOsMainDir + "/.env"
@@ -10,7 +10,7 @@ const (
 	TrailDatabaseFilePath              string = InfiniteOsMainDir + "/trail.db"
 	MarketplaceCatalogItemsDir         string = InfiniteOsMainDir + "/marketplace"
 	MarketplaceCatalogItemsRepoUrl     string = "https://github.com/goinfinite/os-marketplace"
-	MarketplaceCatalogItemsRepoBranch  string = "v1"
+	MarketplaceCatalogItemsRepoBranch  string = "v2"
 	InstallableServicesItemsDir        string = InfiniteOsMainDir + "/services"
 	InstallableServicesItemsRepoUrl    string = "https://github.com/goinfinite/os-services"
 	InstallableServicesItemsRepoBranch string = "v1"
@@ -20,6 +20,8 @@ const (
 	MappingsSecurityRulesConfDir       string = MappingsConfDir + "/security"
 	PkiConfDir                         string = "/app/conf/pki"
 	PhpWebserverMainConfFilePath       string = "/usr/local/lsws/conf/httpd_config.conf"
+	PhpWebserverUsername               string = "nobody"
+	PhpWebserverGroupName              string = "nogroup"
 	AccessTokenCookieKey               string = "os-access-token"
 	UserDataBaseDirectory              string = "/home"
 	DefaultPrimaryVhost                string = "goinfinite.local"

+ 45 - 9
src/infra/helper/runCmd.go

@@ -4,9 +4,11 @@ import (
 	"bytes"
 	"encoding/json"
 	"os/exec"
+	"os/user"
 	"slices"
 	"strconv"
 	"strings"
+	"syscall"
 )
 
 const commandDeadlineExceededError string = "CommandDeadlineExceeded"
@@ -28,16 +30,40 @@ func (e *CmdError) Error() string {
 type RunCmdSettings struct {
 	Command               string
 	Args                  []string
+	Username              string
+	WorkingDirectory      string
 	ShouldRunWithSubShell bool
 	ExecutionTimeoutSecs  uint64
 }
 
+func sysCallCredentialsFactory(
+	username string,
+) (*syscall.Credential, error) {
+	userStruct, err := user.Lookup(username)
+	if err != nil {
+		return nil, err
+	}
+	userId, err := strconv.Atoi(userStruct.Uid)
+	if err != nil {
+		return nil, err
+	}
+	groupId, err := strconv.Atoi(userStruct.Gid)
+	if err != nil {
+		return nil, err
+	}
+
+	return &syscall.Credential{
+		Uid: uint32(userId),
+		Gid: uint32(groupId),
+	}, nil
+}
+
 func prepareCmdExecutor(
-	runConfigs RunCmdSettings,
+	runSettings RunCmdSettings,
 ) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer) {
-	args := runConfigs.Args
-	command := runConfigs.Command
-	if runConfigs.ShouldRunWithSubShell {
+	args := runSettings.Args
+	command := runSettings.Command
+	if runSettings.ShouldRunWithSubShell {
 		args = []string{
 			"-c", "source /etc/profile; " + command + " " + strings.Join(args, " "),
 		}
@@ -45,17 +71,27 @@ func prepareCmdExecutor(
 	}
 
 	executionTimeoutSecs := uint64(1800)
-	if runConfigs.ExecutionTimeoutSecs > 0 && runConfigs.ExecutionTimeoutSecs <= executionTimeoutSecs {
-		executionTimeoutSecs = runConfigs.ExecutionTimeoutSecs
+	if runSettings.ExecutionTimeoutSecs > 0 && runSettings.ExecutionTimeoutSecs <= executionTimeoutSecs {
+		executionTimeoutSecs = runSettings.ExecutionTimeoutSecs
 	}
 
 	args = slices.Concat(
-		[]string{strconv.FormatUint(runConfigs.ExecutionTimeoutSecs, 10), command},
+		[]string{strconv.FormatUint(executionTimeoutSecs, 10), command},
 		args,
 	)
 	command = "timeout"
 
 	cmdExecutor := exec.Command(command, args...)
+	if runSettings.Username != "" && runSettings.Username != "root" {
+		sysCallCredentials, err := sysCallCredentialsFactory(runSettings.Username)
+		if err == nil {
+			cmdExecutor.SysProcAttr = &syscall.SysProcAttr{Credential: sysCallCredentials}
+		}
+	}
+
+	if runSettings.WorkingDirectory != "" {
+		cmdExecutor.Dir = runSettings.WorkingDirectory
+	}
 
 	var stdoutBytesBuffer, stderrBytesBuffer bytes.Buffer
 	cmdExecutor.Stdout = &stdoutBytesBuffer
@@ -66,8 +102,8 @@ func prepareCmdExecutor(
 	return cmdExecutor, &stdoutBytesBuffer, &stderrBytesBuffer
 }
 
-func RunCmd(runConfigs RunCmdSettings) (string, error) {
-	cmdExecutor, stdoutBytesBuffer, stderrBytesBuffer := prepareCmdExecutor(runConfigs)
+func RunCmd(runSettings RunCmdSettings) (string, error) {
+	cmdExecutor, stdoutBytesBuffer, stderrBytesBuffer := prepareCmdExecutor(runSettings)
 
 	err := cmdExecutor.Run()
 	stdoutStr := strings.TrimSpace(stdoutBytesBuffer.String())

+ 63 - 0
src/infra/runtime/runtimeCmdRepo.go

@@ -6,6 +6,7 @@ import (
 	"os"
 	"strings"
 
+	"github.com/goinfinite/os/src/domain/dto"
 	"github.com/goinfinite/os/src/domain/entity"
 	"github.com/goinfinite/os/src/domain/valueObject"
 	infraEnvs "github.com/goinfinite/os/src/infra/envs"
@@ -28,6 +29,68 @@ func NewRuntimeCmdRepo(
 	}
 }
 
+func (repo *RuntimeCmdRepo) RunPhpCommand(
+	runRequest dto.RunPhpCommandRequest,
+) (runResponse dto.RunPhpCommandResponse, err error) {
+	phpVersionEntity, err := repo.runtimeQueryRepo.ReadPhpVersion(runRequest.Hostname)
+	if err != nil {
+		return runResponse, err
+	}
+	phpVersionWithoutDots := phpVersionEntity.Value.GetWithoutDots()
+	if phpVersionWithoutDots == "" {
+		return runResponse, errors.New("PhpVersionNotFound")
+	}
+
+	phpCli := "/usr/local/lsws/lsphp" + phpVersionWithoutDots + "/bin/php"
+	if !infraHelper.FileExists(phpCli) {
+		return runResponse, errors.New("PhpCliNotFound")
+	}
+
+	timeoutSecs := uint64(600)
+	if runRequest.TimeoutSecs != nil {
+		timeoutSecs = *runRequest.TimeoutSecs
+	}
+	workingDir := infraEnvs.PrimaryPublicDir
+	if !infraHelper.IsPrimaryVirtualHost(runRequest.Hostname) {
+		workingDir += "/" + runRequest.Hostname.String()
+	}
+	if !infraHelper.FileExists(workingDir) {
+		workingDir = infraEnvs.PrimaryPublicDir
+	}
+
+	cmdOutput, cmdErr := infraHelper.RunCmd(infraHelper.RunCmdSettings{
+		Command:               phpCli,
+		Args:                  []string{runRequest.Command.String()},
+		Username:              infraEnvs.PhpWebserverUsername,
+		WorkingDirectory:      workingDir,
+		ShouldRunWithSubShell: true,
+		ExecutionTimeoutSecs:  timeoutSecs,
+	})
+	stdOutput, err := valueObject.NewUnixCommandOutput(cmdOutput)
+	if err != nil {
+		return runResponse, err
+	}
+
+	if errorMessage, assertOk := cmdErr.(*infraHelper.CmdError); assertOk {
+		stdError, err := valueObject.NewUnixCommandOutput(errorMessage.StdErr)
+		if err != nil {
+			return runResponse, err
+		}
+
+		runResponse.StdOutput = &stdOutput
+		runResponse.StdError = &stdError
+		runResponse.ExitCode = &errorMessage.ExitCode
+		return runResponse, nil
+	}
+
+	successExitCode := 0
+	return dto.RunPhpCommandResponse{
+		StdOutput: &stdOutput,
+		StdError:  nil,
+		ExitCode:  &successExitCode,
+	}, nil
+}
+
 func (repo *RuntimeCmdRepo) restartPhpWebserver() error {
 	phpSvcName, _ := valueObject.NewServiceName("php-webserver")
 	servicesCmdRepo := servicesInfra.NewServicesCmdRepo(repo.persistentDbSvc)

+ 2 - 2
src/infra/runtime/runtimeQueryRepo.go

@@ -22,12 +22,12 @@ func (repo RuntimeQueryRepo) GetVirtualHostPhpConfFilePath(
 	primaryVhostPhpConfFilePathStr := "/app/conf/php-webserver/primary.conf"
 	vhostPhpConfFilePathStr := "/app/conf/php-webserver/" + hostname.String() + ".conf"
 
-	primaryVhost, err := infraHelper.ReadPrimaryVirtualHostHostname()
+	primaryVirtualHostHostname, err := infraHelper.ReadPrimaryVirtualHostHostname()
 	if err != nil {
 		return vhostPhpConfFilePath, errors.New("PrimaryVhostNotFound: " + err.Error())
 	}
 
-	if hostname.String() == primaryVhost.String() {
+	if hostname == primaryVirtualHostHostname {
 		vhostPhpConfFilePathStr = primaryVhostPhpConfFilePathStr
 	}
 

+ 34 - 4
src/infra/ssl/sslCmdRepo.go

@@ -45,6 +45,10 @@ func (repo *SslCmdRepo) dnsFilterFunctionalHostnames(
 	for _, vhostHostname := range vhostHostnames {
 		wwwVirtualHostHostname, err := valueObject.NewFqdn("www." + vhostHostname.String())
 		if err != nil {
+			slog.Debug(
+				"InvalidWwwVirtualHostHostname",
+				slog.String("fqdn", vhostHostname.String()),
+			)
 			continue
 		}
 
@@ -56,12 +60,28 @@ func (repo *SslCmdRepo) dnsFilterFunctionalHostnames(
 		vhostHostnameStr := vhostHostname.String()
 
 		hostnameRecords, err := infraHelper.DnsLookup(vhostHostnameStr, nil)
-		if err != nil || len(hostnameRecords) == 0 {
+		if err != nil {
+			slog.Debug(
+				"DnsLookupFailed",
+				slog.String("fqdn", vhostHostnameStr),
+				slog.String("error", err.Error()),
+			)
+			continue
+		}
+
+		if len(hostnameRecords) == 0 {
+			slog.Debug("NoDnsRecordsFound", slog.String("fqdn", vhostHostnameStr))
 			continue
 		}
 
-		for _, record := range hostnameRecords {
-			if record != serverPublicIpAddressStr {
+		for _, dnsRecord := range hostnameRecords {
+			if dnsRecord != serverPublicIpAddressStr {
+				slog.Debug(
+					"DnsRecordDoesNotMatchServerIpAddress",
+					slog.String("fqdn", vhostHostnameStr),
+					slog.String("dnsRecord", dnsRecord),
+					slog.String("serverPublicIpAddress", serverPublicIpAddressStr),
+				)
 				continue
 			}
 
@@ -306,6 +326,11 @@ func (repo *SslCmdRepo) Create(
 		return sslPairId, errors.New("SslPairNotFound: " + err.Error())
 	}
 
+	err = infraHelper.ReloadWebServer()
+	if err != nil {
+		return sslPairId, errors.New("ReloadWebServerError: " + err.Error())
+	}
+
 	return sslPairEntity.Id, nil
 }
 
@@ -357,7 +382,12 @@ func (repo *SslCmdRepo) ReplaceWithSelfSigned(vhostHostname valueObject.Fqdn) er
 		return errors.New("PkiConfDirError: " + err.Error())
 	}
 
-	return infraHelper.CreateSelfSignedSsl(pkiConfDir, vhostHostname, aliasesHostnames)
+	err = infraHelper.CreateSelfSignedSsl(pkiConfDir, vhostHostname, aliasesHostnames)
+	if err != nil {
+		return errors.New("CreateSelfSignedSslError: " + err.Error())
+	}
+
+	return infraHelper.ReloadWebServer()
 }
 
 func (repo *SslCmdRepo) Delete(sslPairId valueObject.SslPairId) error {

+ 15 - 0
src/infra/vhost/mappingCmdRepo.go

@@ -3,6 +3,7 @@ package vhostInfra
 import (
 	"errors"
 	"os"
+	"slices"
 	"strings"
 	"text/template"
 
@@ -495,6 +496,9 @@ func (repo *MappingCmdRepo) Update(updateDto dto.UpdateMapping) error {
 	if updateDto.MappingSecurityRuleId != nil {
 		updateMap["mapping_security_rule_id"] = updateDto.MappingSecurityRuleId.Uint64()
 	}
+	if slices.Contains(updateDto.ClearableFields, "mappingSecurityRuleId") {
+		updateMap["mapping_security_rule_id"] = nil
+	}
 
 	err = repo.persistentDbSvc.Handler.
 		Model(&dbModel.Mapping{}).
@@ -751,6 +755,9 @@ func (repo *MappingCmdRepo) UpdateSecurityRule(
 	if updateDto.Description != nil {
 		updateMap["description"] = updateDto.Description.String()
 	}
+	if slices.Contains(updateDto.ClearableFields, "description") {
+		updateMap["description"] = nil
+	}
 
 	if updateDto.RpsSoftLimitPerIp != nil {
 		updateMap["rps_soft_limit_per_ip"] = *updateDto.RpsSoftLimitPerIp
@@ -780,6 +787,14 @@ func (repo *MappingCmdRepo) UpdateSecurityRule(
 		updateMap["response_code_on_max_connections"] = *updateDto.ResponseCodeOnMaxConnections
 	}
 
+	if slices.Contains(updateDto.ClearableFields, "allowedIps") {
+		updateMap["allowed_ips"] = []string{}
+	}
+
+	if slices.Contains(updateDto.ClearableFields, "blockedIps") {
+		updateMap["blocked_ips"] = []string{}
+	}
+
 	err := repo.persistentDbSvc.Handler.Model(&dbModel.MappingSecurityRule{}).
 		Where("id = ?", updateDto.Id.Uint64()).Updates(updateMap).Error
 	if err != nil {

+ 1 - 2
src/infra/webServer/webServerSetup.go

@@ -16,7 +16,6 @@ import (
 	o11yInfra "github.com/goinfinite/os/src/infra/o11y"
 	servicesInfra "github.com/goinfinite/os/src/infra/services"
 	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
-	"github.com/goinfinite/os/src/presentation/service"
 )
 
 type WebServerSetup struct {
@@ -138,7 +137,7 @@ func (ws *WebServerSetup) FirstSetup() {
 	updateServiceDto := dto.NewUpdateService(
 		nginxServiceName, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
 		nil, nil, &nginxAutoStart, nil, nil, nil, nil, nil, nil,
-		service.LocalOperatorAccountId, service.LocalOperatorIpAddress,
+		valueObject.AccountIdSystem, valueObject.IpAddressSystem,
 	)
 	err = servicesCmdRepo.Update(updateServiceDto)
 	if err != nil {

+ 1 - 1
src/presentation/api/api.go

@@ -14,7 +14,7 @@ const (
 )
 
 // @title			OsApi
-// @version			0.2.5
+// @version			0.2.6
 // @description		Infinite OS API
 // @termsOfService	https://goinfinite.net/tos/
 

+ 15 - 15
src/presentation/api/controller/account.go

@@ -3,12 +3,12 @@ package apiController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
 type AccountController struct {
-	accountService *service.AccountService
+	accountLiaison *liaison.AccountLiaison
 }
 
 func NewAccountController(
@@ -16,7 +16,7 @@ func NewAccountController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *AccountController {
 	return &AccountController{
-		accountService: service.NewAccountService(persistentDbSvc, trailDbSvc),
+		accountLiaison: liaison.NewAccountLiaison(persistentDbSvc, trailDbSvc),
 	}
 }
 
@@ -43,8 +43,8 @@ func (controller *AccountController) Read(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.accountService.Read(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.accountLiaison.Read(requestInputData),
 	)
 }
 
@@ -64,8 +64,8 @@ func (controller *AccountController) Create(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.accountService.Create(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.accountLiaison.Create(requestInputData),
 	)
 }
 
@@ -85,8 +85,8 @@ func (controller *AccountController) Update(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.accountService.Update(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.accountLiaison.Update(requestInputData),
 	)
 }
 
@@ -106,8 +106,8 @@ func (controller *AccountController) Delete(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.accountService.Delete(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.accountLiaison.Delete(requestInputData),
 	)
 }
 
@@ -127,8 +127,8 @@ func (controller *AccountController) CreateSecureAccessPublicKey(c echo.Context)
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.accountService.CreateSecureAccessPublicKey(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.accountLiaison.CreateSecureAccessPublicKey(requestInputData),
 	)
 }
 
@@ -148,7 +148,7 @@ func (controller *AccountController) DeleteSecureAccessPublicKey(c echo.Context)
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.accountService.DeleteSecureAccessPublicKey(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.accountLiaison.DeleteSecureAccessPublicKey(requestInputData),
 	)
 }

+ 5 - 5
src/presentation/api/controller/authentication.go

@@ -3,12 +3,12 @@ package apiController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
 type AuthenticationController struct {
-	authenticationService *service.AuthenticationService
+	authenticationLiaison *liaison.AuthenticationLiaison
 }
 
 func NewAuthenticationController(
@@ -16,7 +16,7 @@ func NewAuthenticationController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *AuthenticationController {
 	return &AuthenticationController{
-		authenticationService: service.NewAuthenticationService(
+		authenticationLiaison: liaison.NewAuthenticationLiaison(
 			persistentDbSvc, trailDbSvc,
 		),
 	}
@@ -38,7 +38,7 @@ func (controller *AuthenticationController) Login(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.authenticationService.Login(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.authenticationLiaison.Login(requestInputData),
 	)
 }

+ 10 - 10
src/presentation/api/controller/cron.go

@@ -3,19 +3,19 @@ package apiController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
 type CronController struct {
-	cronService *service.CronService
+	cronLiaison *liaison.CronLiaison
 }
 
 func NewCronController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *CronController {
 	return &CronController{
-		cronService: service.NewCronService(trailDbSvc),
+		cronLiaison: liaison.NewCronLiaison(trailDbSvc),
 	}
 }
 
@@ -41,7 +41,7 @@ func (controller *CronController) Read(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(c, controller.cronService.Read(requestInputData))
+	return apiHelper.LiaisonResponseWrapper(c, controller.cronLiaison.Read(requestInputData))
 }
 
 // CreateCron    godoc
@@ -60,8 +60,8 @@ func (controller *CronController) Create(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.cronService.Create(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.cronLiaison.Create(requestInputData),
 	)
 }
 
@@ -81,8 +81,8 @@ func (controller *CronController) Update(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.cronService.Update(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.cronLiaison.Update(requestInputData),
 	)
 }
 
@@ -102,7 +102,7 @@ func (controller *CronController) Delete(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.cronService.Delete(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.cronLiaison.Delete(requestInputData),
 	)
 }

+ 13 - 13
src/presentation/api/controller/database.go

@@ -4,7 +4,7 @@ import (
 	"github.com/goinfinite/os/src/domain/valueObject"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 
 	tkPresentation "github.com/goinfinite/tk/src/presentation"
@@ -12,7 +12,7 @@ import (
 
 type DatabaseController struct {
 	persistentDbService *internalDbInfra.PersistentDatabaseService
-	dbService           *service.DatabaseService
+	databaseLiaison     *liaison.DatabaseLiaison
 }
 
 func NewDatabaseController(
@@ -21,7 +21,7 @@ func NewDatabaseController(
 ) *DatabaseController {
 	return &DatabaseController{
 		persistentDbService: persistentDbService,
-		dbService: service.NewDatabaseService(
+		databaseLiaison: liaison.NewDatabaseLiaison(
 			persistentDbService, trailDbSvc,
 		),
 	}
@@ -50,8 +50,8 @@ func (controller *DatabaseController) Read(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.dbService.Read(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.databaseLiaison.Read(requestInputData),
 	)
 }
 
@@ -72,8 +72,8 @@ func (controller *DatabaseController) Create(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.dbService.Create(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.databaseLiaison.Create(requestInputData),
 	)
 }
 
@@ -94,8 +94,8 @@ func (controller *DatabaseController) Delete(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.dbService.Delete(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.databaseLiaison.Delete(requestInputData),
 	)
 }
 
@@ -127,8 +127,8 @@ func (controller *DatabaseController) CreateUser(c echo.Context) error {
 		)
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.dbService.CreateUser(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.databaseLiaison.CreateUser(requestInputData),
 	)
 }
 
@@ -150,7 +150,7 @@ func (controller *DatabaseController) DeleteUser(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.dbService.DeleteUser(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.databaseLiaison.DeleteUser(requestInputData),
 	)
 }

+ 11 - 11
src/presentation/api/controller/marketplace.go

@@ -10,12 +10,12 @@ import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	marketplaceInfra "github.com/goinfinite/os/src/infra/marketplace"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
 type MarketplaceController struct {
-	marketplaceService *service.MarketplaceService
+	marketplaceLiaison *liaison.MarketplaceLiaison
 	persistentDbSvc    *internalDbInfra.PersistentDatabaseService
 }
 
@@ -24,7 +24,7 @@ func NewMarketplaceController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *MarketplaceController {
 	return &MarketplaceController{
-		marketplaceService: service.NewMarketplaceService(persistentDbSvc, trailDbSvc),
+		marketplaceLiaison: liaison.NewMarketplaceLiaison(persistentDbSvc, trailDbSvc),
 		persistentDbSvc:    persistentDbSvc,
 	}
 }
@@ -53,8 +53,8 @@ func (controller *MarketplaceController) ReadCatalog(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.marketplaceService.ReadCatalog(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.marketplaceLiaison.ReadCatalog(requestInputData),
 	)
 }
 
@@ -168,8 +168,8 @@ func (controller *MarketplaceController) InstallCatalogItem(c echo.Context) erro
 		)
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.marketplaceService.InstallCatalogItem(requestInputData, true),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.marketplaceLiaison.InstallCatalogItem(requestInputData, true),
 	)
 }
 
@@ -197,8 +197,8 @@ func (controller *MarketplaceController) ReadInstalledItems(c echo.Context) erro
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.marketplaceService.ReadInstalledItems(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.marketplaceLiaison.ReadInstalledItems(requestInputData),
 	)
 }
 
@@ -219,8 +219,8 @@ func (controller *MarketplaceController) DeleteInstalledItem(c echo.Context) err
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.marketplaceService.DeleteInstalledItem(requestInputData, true),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.marketplaceLiaison.DeleteInstalledItem(requestInputData, true),
 	)
 }
 

+ 4 - 4
src/presentation/api/controller/o11y.go

@@ -3,19 +3,19 @@ package apiController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
 type O11yController struct {
-	o11yService *service.O11yService
+	o11yLiaison *liaison.O11yLiaison
 }
 
 func NewO11yController(
 	transientDbService *internalDbInfra.TransientDatabaseService,
 ) *O11yController {
 	return &O11yController{
-		o11yService: service.NewO11yService(transientDbService),
+		o11yLiaison: liaison.NewO11yLiaison(transientDbService),
 	}
 }
 
@@ -29,5 +29,5 @@ func NewO11yController(
 // @Success      200 {object} entity.O11yOverview
 // @Router       /v1/o11y/overview/ [get]
 func (controller *O11yController) ReadOverview(c echo.Context) error {
-	return apiHelper.ServiceResponseWrapper(c, controller.o11yService.ReadOverview())
+	return apiHelper.LiaisonResponseWrapper(c, controller.o11yLiaison.ReadOverview())
 }

+ 38 - 17
src/presentation/api/controller/runtime.go

@@ -10,12 +10,12 @@ import (
 	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
 type RuntimeController struct {
-	runtimeService *service.RuntimeService
+	runtimeLiaison *liaison.RuntimeLiaison
 }
 
 func NewRuntimeController(
@@ -23,7 +23,7 @@ func NewRuntimeController(
 	trailDbService *internalDbInfra.TrailDatabaseService,
 ) *RuntimeController {
 	return &RuntimeController{
-		runtimeService: service.NewRuntimeService(persistentDbService, trailDbService),
+		runtimeLiaison: liaison.NewRuntimeLiaison(persistentDbService, trailDbService),
 	}
 }
 
@@ -43,27 +43,27 @@ func (controller *RuntimeController) ReadPhpConfigs(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.runtimeService.ReadPhpConfigs(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.runtimeLiaison.ReadPhpConfigs(requestInputData),
 	)
 }
 
-func (controller *RuntimeController) parsePhpModules(rawPhpModules interface{}) (
+func (controller *RuntimeController) parsePhpModules(rawPhpModules any) (
 	[]entity.PhpModule, error,
 ) {
 	modules := []entity.PhpModule{}
 
-	rawModulesSlice, assertOk := rawPhpModules.([]interface{})
+	rawModulesSlice, assertOk := rawPhpModules.([]any)
 	if !assertOk {
-		rawUniqueModule, assertOk := rawPhpModules.(map[string]interface{})
+		rawUniqueModule, assertOk := rawPhpModules.(map[string]any)
 		if !assertOk {
 			return modules, errors.New("InvalidPhpModulesStructure")
 		}
-		rawModulesSlice = []interface{}{rawUniqueModule}
+		rawModulesSlice = []any{rawUniqueModule}
 	}
 
 	for _, rawModule := range rawModulesSlice {
-		rawModuleMap, assertOk := rawModule.(map[string]interface{})
+		rawModuleMap, assertOk := rawModule.(map[string]any)
 		if !assertOk {
 			slog.Debug("PhpModuleIsNotAnInterface")
 			continue
@@ -87,22 +87,22 @@ func (controller *RuntimeController) parsePhpModules(rawPhpModules interface{})
 	return modules, nil
 }
 
-func (controller *RuntimeController) parsePhpSettings(rawPhpSettings interface{}) (
+func (controller *RuntimeController) parsePhpSettings(rawPhpSettings any) (
 	[]entity.PhpSetting, error,
 ) {
 	settings := []entity.PhpSetting{}
 
-	rawSettingsSlice, assertOk := rawPhpSettings.([]interface{})
+	rawSettingsSlice, assertOk := rawPhpSettings.([]any)
 	if !assertOk {
-		rawUniqueSetting, assertOk := rawPhpSettings.(map[string]interface{})
+		rawUniqueSetting, assertOk := rawPhpSettings.(map[string]any)
 		if !assertOk {
 			return settings, errors.New("InvalidPhpSettingsStructure")
 		}
-		rawSettingsSlice = []interface{}{rawUniqueSetting}
+		rawSettingsSlice = []any{rawUniqueSetting}
 	}
 
 	for _, rawSetting := range rawSettingsSlice {
-		rawSettingMap, assertOk := rawSetting.(map[string]interface{})
+		rawSettingMap, assertOk := rawSetting.(map[string]any)
 		if !assertOk {
 			slog.Debug("PhpSettingIsNotAnInterface")
 			continue
@@ -170,7 +170,28 @@ func (controller *RuntimeController) UpdatePhpConfigs(c echo.Context) error {
 		requestInputData["settings"] = phpSettings
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.runtimeService.UpdatePhpConfigs(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.runtimeLiaison.UpdatePhpConfigs(requestInputData),
+	)
+}
+
+// RunPhpCommand godoc
+// @Summary      RunPhpCommand
+// @Description  Run a php command as the webserver user for a given hostname. <br />CAUTION: This endpoint allows for arbitrary code execution (ACE) and is therefore disabled by default. <br />To enable this endpoint, set the "ENABLE_API_RUNTIME_PHP_RUN_CMD" environment variable to "true" when starting the API/container.<br />Only super admin accounts can use this endpoint.
+// @Tags         runtime
+// @Accept       json
+// @Produce      json
+// @Security     Bearer
+// @Param        runPhpCmdDto	body dto.RunPhpCommandRequest	true	"Hostname and command are required. Timeout is optional."
+// @Success      200 {object} dto.RunPhpCommandResponse
+// @Router       /v1/runtime/php/run/ [post]
+func (controller *RuntimeController) RunPhpCommand(echoContext echo.Context) error {
+	requestInputData, err := apiHelper.ReadRequestInputData(echoContext)
+	if err != nil {
+		return err
+	}
+
+	return apiHelper.LiaisonResponseWrapper(
+		echoContext, controller.runtimeLiaison.RunPhpCommand(requestInputData),
 	)
 }

+ 7 - 7
src/presentation/api/controller/scheduledTask.go

@@ -12,12 +12,12 @@ import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	scheduledTaskInfra "github.com/goinfinite/os/src/infra/scheduledTask"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
 type ScheduledTaskController struct {
-	scheduledTaskService *service.ScheduledTaskService
+	scheduledTaskLiaison *liaison.ScheduledTaskLiaison
 	persistentDbSvc      *internalDbInfra.PersistentDatabaseService
 }
 
@@ -25,7 +25,7 @@ func NewScheduledTaskController(
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
 ) *ScheduledTaskController {
 	return &ScheduledTaskController{
-		scheduledTaskService: service.NewScheduledTaskService(persistentDbSvc),
+		scheduledTaskLiaison: liaison.NewScheduledTaskLiaison(persistentDbSvc),
 		persistentDbSvc:      persistentDbSvc,
 	}
 }
@@ -90,8 +90,8 @@ func (controller *ScheduledTaskController) Read(c echo.Context) error {
 		requestInputData["taskTags"] = taskTags
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.scheduledTaskService.Read(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.scheduledTaskLiaison.Read(requestInputData),
 	)
 }
 
@@ -111,8 +111,8 @@ func (controller *ScheduledTaskController) Update(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.scheduledTaskService.Update(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.scheduledTaskLiaison.Update(requestInputData),
 	)
 }
 

+ 17 - 17
src/presentation/api/controller/services.go

@@ -12,13 +12,13 @@ import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	servicesInfra "github.com/goinfinite/os/src/infra/services"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	sharedHelper "github.com/goinfinite/os/src/presentation/shared/helper"
 	"github.com/labstack/echo/v4"
 )
 
 type ServicesController struct {
-	servicesService *service.ServicesService
+	servicesLiaison *liaison.ServicesLiaison
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService
 }
 
@@ -27,7 +27,7 @@ func NewServicesController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *ServicesController {
 	return &ServicesController{
-		servicesService: service.NewServicesService(persistentDbService, trailDbSvc),
+		servicesLiaison: liaison.NewServicesLiaison(persistentDbService, trailDbSvc),
 		persistentDbSvc: persistentDbService,
 	}
 }
@@ -56,8 +56,8 @@ func (controller *ServicesController) ReadInstalledItems(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.servicesService.ReadInstalledItems(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.servicesLiaison.ReadInstalledItems(requestInputData),
 	)
 }
 
@@ -85,8 +85,8 @@ func (controller *ServicesController) ReadInstallablesItems(c echo.Context) erro
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.servicesService.ReadInstallableItems(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.servicesLiaison.ReadInstallableItems(requestInputData),
 	)
 }
 
@@ -268,8 +268,8 @@ func (controller *ServicesController) CreateInstallable(c echo.Context) error {
 	}
 	requestInputData["portBindings"] = portBindings
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.servicesService.CreateInstallable(requestInputData, true),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.servicesLiaison.CreateInstallable(requestInputData, true),
 	)
 }
 
@@ -304,13 +304,13 @@ func (controller *ServicesController) CreateCustom(c echo.Context) error {
 			requestInputData["portBindings"],
 		)
 		if err != nil {
-			return apiHelper.ResponseWrapper(c, http.StatusBadRequest, err)
+			return apiHelper.ResponseWrapper(c, http.StatusBadRequest, err.Error())
 		}
 	}
 	requestInputData["portBindings"] = portBindings
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.servicesService.CreateCustom(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.servicesLiaison.CreateCustom(requestInputData),
 	)
 }
 
@@ -343,13 +343,13 @@ func (controller *ServicesController) Update(c echo.Context) error {
 			requestInputData["portBindings"],
 		)
 		if err != nil {
-			return apiHelper.ResponseWrapper(c, http.StatusBadRequest, err)
+			return apiHelper.ResponseWrapper(c, http.StatusBadRequest, err.Error())
 		}
 		requestInputData["portBindings"] = rawPortBindings
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.servicesService.Update(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.servicesLiaison.Update(requestInputData),
 	)
 }
 
@@ -370,8 +370,8 @@ func (controller *ServicesController) Delete(c echo.Context) error {
 	}
 	requestInputData["name"] = requestInputData["svcName"]
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.servicesService.Delete(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.servicesLiaison.Delete(requestInputData),
 	)
 }
 

+ 3 - 3
src/presentation/api/controller/setup.go

@@ -10,7 +10,7 @@ import (
 	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
@@ -62,7 +62,7 @@ func (controller *SetupController) Setup(c echo.Context) error {
 
 	isSuperAdmin := false
 
-	operatorIpAddress := service.LocalOperatorIpAddress
+	operatorIpAddress := liaison.LocalOperatorIpAddress
 	if requestBody["operatorIpAddress"] != nil {
 		operatorIpAddress, err = valueObject.NewIpAddress(
 			requestBody["operatorIpAddress"],
@@ -73,7 +73,7 @@ func (controller *SetupController) Setup(c echo.Context) error {
 	}
 
 	createDto := dto.NewCreateAccount(
-		username, password, isSuperAdmin, service.LocalOperatorAccountId,
+		username, password, isSuperAdmin, liaison.LocalOperatorAccountId,
 		operatorIpAddress,
 	)
 

+ 11 - 11
src/presentation/api/controller/ssl.go

@@ -11,7 +11,7 @@ import (
 	sslInfra "github.com/goinfinite/os/src/infra/ssl"
 	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	sharedHelper "github.com/goinfinite/os/src/presentation/shared/helper"
 	"github.com/labstack/echo/v4"
 )
@@ -19,7 +19,7 @@ import (
 type SslController struct {
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService
 	transientDbSvc  *internalDbInfra.TransientDatabaseService
-	sslService      *service.SslService
+	sslLiaison      *liaison.SslLiaison
 }
 
 func NewSslController(
@@ -30,7 +30,7 @@ func NewSslController(
 	return &SslController{
 		persistentDbSvc: persistentDbSvc,
 		transientDbSvc:  transientDbSvc,
-		sslService: service.NewSslService(
+		sslLiaison: liaison.NewSslLiaison(
 			persistentDbSvc, transientDbSvc, trailDbSvc,
 		),
 	}
@@ -69,8 +69,8 @@ func (controller *SslController) Read(c echo.Context) error {
 		)
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.sslService.Read(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.sslLiaison.Read(requestInputData),
 	)
 }
 
@@ -144,8 +144,8 @@ func (controller *SslController) Create(c echo.Context) error {
 		requestInputData["chainCertificates"] = nil
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.sslService.Create(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.sslLiaison.Create(requestInputData),
 	)
 }
 
@@ -165,8 +165,8 @@ func (controller *SslController) CreatePubliclyTrusted(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.sslService.CreatePubliclyTrusted(requestInputData, true),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.sslLiaison.CreatePubliclyTrusted(requestInputData, true),
 	)
 }
 
@@ -186,8 +186,8 @@ func (controller *SslController) Delete(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.sslService.Delete(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.sslLiaison.Delete(requestInputData),
 	)
 }
 

+ 27 - 27
src/presentation/api/controller/virtualHost.go

@@ -3,7 +3,7 @@ package apiController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	apiHelper "github.com/goinfinite/os/src/presentation/api/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 
 	tkValueObject "github.com/goinfinite/tk/src/domain/valueObject"
@@ -11,7 +11,7 @@ import (
 )
 
 type VirtualHostController struct {
-	virtualHostService *service.VirtualHostService
+	virtualHostLiaison *liaison.VirtualHostLiaison
 }
 
 func NewVirtualHostController(
@@ -19,7 +19,7 @@ func NewVirtualHostController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *VirtualHostController {
 	return &VirtualHostController{
-		virtualHostService: service.NewVirtualHostService(persistentDbSvc, trailDbSvc),
+		virtualHostLiaison: liaison.NewVirtualHostLiaison(persistentDbSvc, trailDbSvc),
 	}
 }
 
@@ -50,8 +50,8 @@ func (controller *VirtualHostController) Read(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.Read(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.Read(requestInputData),
 	)
 }
 
@@ -71,8 +71,8 @@ func (controller *VirtualHostController) Create(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.Create(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.Create(requestInputData),
 	)
 }
 
@@ -92,8 +92,8 @@ func (controller *VirtualHostController) Update(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.Update(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.Update(requestInputData),
 	)
 }
 
@@ -113,8 +113,8 @@ func (controller *VirtualHostController) Delete(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.Delete(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.Delete(requestInputData),
 	)
 }
 
@@ -145,8 +145,8 @@ func (controller *VirtualHostController) ReadWithMappings(c echo.Context) error
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.ReadWithMappings(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.ReadWithMappings(requestInputData),
 	)
 }
 
@@ -166,8 +166,8 @@ func (controller *VirtualHostController) CreateMapping(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.CreateMapping(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.CreateMapping(requestInputData),
 	)
 }
 
@@ -187,8 +187,8 @@ func (controller *VirtualHostController) UpdateMapping(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.UpdateMapping(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.UpdateMapping(requestInputData),
 	)
 }
 
@@ -208,8 +208,8 @@ func (controller *VirtualHostController) DeleteMapping(c echo.Context) error {
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.DeleteMapping(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.DeleteMapping(requestInputData),
 	)
 }
 
@@ -239,8 +239,8 @@ func (controller *VirtualHostController) ReadMappingSecurityRules(c echo.Context
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.ReadMappingSecurityRules(
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.ReadMappingSecurityRules(
 			requestInputData,
 		),
 	)
@@ -274,8 +274,8 @@ func (controller *VirtualHostController) CreateMappingSecurityRule(c echo.Contex
 		)
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.CreateMappingSecurityRule(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.CreateMappingSecurityRule(requestInputData),
 	)
 }
 
@@ -307,8 +307,8 @@ func (controller *VirtualHostController) UpdateMappingSecurityRule(c echo.Contex
 		)
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.UpdateMappingSecurityRule(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.UpdateMappingSecurityRule(requestInputData),
 	)
 }
 
@@ -328,7 +328,7 @@ func (controller *VirtualHostController) DeleteMappingSecurityRule(c echo.Contex
 		return err
 	}
 
-	return apiHelper.ServiceResponseWrapper(
-		c, controller.virtualHostService.DeleteMappingSecurityRule(requestInputData),
+	return apiHelper.LiaisonResponseWrapper(
+		c, controller.virtualHostLiaison.DeleteMappingSecurityRule(requestInputData),
 	)
 }

+ 68 - 1
src/presentation/api/docs/docs.go

@@ -1432,6 +1432,45 @@ const docTemplate = `{
                 }
             }
         },
+        "/v1/runtime/php/run/": {
+            "post": {
+                "security": [
+                    {
+                        "Bearer": []
+                    }
+                ],
+                "description": "Run a php command as the webserver user for a given hostname. \u003cbr /\u003eCAUTION: This endpoint allows for arbitrary code execution (ACE) and is therefore disabled by default. \u003cbr /\u003eTo enable this endpoint, set the \"ENABLE_API_RUNTIME_PHP_RUN_CMD\" environment variable to \"true\" when starting the API/container.\u003cbr /\u003eOnly super admin accounts can use this endpoint.",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "runtime"
+                ],
+                "summary": "RunPhpCommand",
+                "parameters": [
+                    {
+                        "description": "Hostname and command are required. Timeout is optional.",
+                        "name": "runPhpCmdDto",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/dto.RunPhpCommandRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/dto.RunPhpCommandResponse"
+                        }
+                    }
+                }
+            }
+        },
         "/v1/runtime/php/{hostname}/": {
             "get": {
                 "security": [
@@ -3581,6 +3620,34 @@ const docTemplate = `{
                 }
             }
         },
+        "dto.RunPhpCommandRequest": {
+            "type": "object",
+            "properties": {
+                "command": {
+                    "type": "string"
+                },
+                "hostname": {
+                    "type": "string"
+                },
+                "timeoutSecs": {
+                    "type": "integer"
+                }
+            }
+        },
+        "dto.RunPhpCommandResponse": {
+            "type": "object",
+            "properties": {
+                "exitCode": {
+                    "type": "integer"
+                },
+                "stdErr": {
+                    "type": "string"
+                },
+                "stdOut": {
+                    "type": "string"
+                }
+            }
+        },
         "dto.UnixFileBranch": {
             "type": "object",
             "properties": {
@@ -4913,7 +4980,7 @@ const docTemplate = `{
 
 // SwaggerInfo holds exported Swagger Info so clients can modify it
 var SwaggerInfo = &swag.Spec{
-	Version:          "0.2.5",
+	Version:          "0.2.6",
 	Host:             "localhost:1618",
 	BasePath:         "/api",
 	Schemes:          []string{},

+ 68 - 1
src/presentation/api/docs/swagger.json

@@ -13,7 +13,7 @@
             "name": "Eclipse Public License v2.0",
             "url": "https://www.eclipse.org/legal/epl-2.0/"
         },
-        "version": "0.2.5"
+        "version": "0.2.6"
     },
     "host": "localhost:1618",
     "basePath": "/api",
@@ -1426,6 +1426,45 @@
                 }
             }
         },
+        "/v1/runtime/php/run/": {
+            "post": {
+                "security": [
+                    {
+                        "Bearer": []
+                    }
+                ],
+                "description": "Run a php command as the webserver user for a given hostname. \u003cbr /\u003eCAUTION: This endpoint allows for arbitrary code execution (ACE) and is therefore disabled by default. \u003cbr /\u003eTo enable this endpoint, set the \"ENABLE_API_RUNTIME_PHP_RUN_CMD\" environment variable to \"true\" when starting the API/container.\u003cbr /\u003eOnly super admin accounts can use this endpoint.",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "runtime"
+                ],
+                "summary": "RunPhpCommand",
+                "parameters": [
+                    {
+                        "description": "Hostname and command are required. Timeout is optional.",
+                        "name": "runPhpCmdDto",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/dto.RunPhpCommandRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/dto.RunPhpCommandResponse"
+                        }
+                    }
+                }
+            }
+        },
         "/v1/runtime/php/{hostname}/": {
             "get": {
                 "security": [
@@ -3575,6 +3614,34 @@
                 }
             }
         },
+        "dto.RunPhpCommandRequest": {
+            "type": "object",
+            "properties": {
+                "command": {
+                    "type": "string"
+                },
+                "hostname": {
+                    "type": "string"
+                },
+                "timeoutSecs": {
+                    "type": "integer"
+                }
+            }
+        },
+        "dto.RunPhpCommandResponse": {
+            "type": "object",
+            "properties": {
+                "exitCode": {
+                    "type": "integer"
+                },
+                "stdErr": {
+                    "type": "string"
+                },
+                "stdOut": {
+                    "type": "string"
+                }
+            }
+        },
         "dto.UnixFileBranch": {
             "type": "object",
             "properties": {

+ 47 - 1
src/presentation/api/docs/swagger.yaml

@@ -462,6 +462,24 @@ definitions:
           $ref: '#/definitions/entity.VirtualHost'
         type: array
     type: object
+  dto.RunPhpCommandRequest:
+    properties:
+      command:
+        type: string
+      hostname:
+        type: string
+      timeoutSecs:
+        type: integer
+    type: object
+  dto.RunPhpCommandResponse:
+    properties:
+      exitCode:
+        type: integer
+      stdErr:
+        type: string
+      stdOut:
+        type: string
+    type: object
   dto.UnixFileBranch:
     properties:
       branches:
@@ -1353,7 +1371,7 @@ info:
     url: https://www.eclipse.org/legal/epl-2.0/
   termsOfService: https://goinfinite.net/tos/
   title: OsApi
-  version: 0.2.5
+  version: 0.2.6
 paths:
   /v1/account/:
     get:
@@ -2302,6 +2320,34 @@ paths:
       summary: UpdatePhpConfigs
       tags:
       - runtime
+  /v1/runtime/php/run/:
+    post:
+      consumes:
+      - application/json
+      description: 'Run a php command as the webserver user for a given hostname.
+        <br />CAUTION: This endpoint allows for arbitrary code execution (ACE) and
+        is therefore disabled by default. <br />To enable this endpoint, set the "ENABLE_API_RUNTIME_PHP_RUN_CMD"
+        environment variable to "true" when starting the API/container.<br />Only
+        super admin accounts can use this endpoint.'
+      parameters:
+      - description: Hostname and command are required. Timeout is optional.
+        in: body
+        name: runPhpCmdDto
+        required: true
+        schema:
+          $ref: '#/definitions/dto.RunPhpCommandRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/dto.RunPhpCommandResponse'
+      security:
+      - Bearer: []
+      summary: RunPhpCommand
+      tags:
+      - runtime
   /v1/scheduled-task/:
     get:
       consumes:

+ 9 - 9
src/presentation/api/helper/serviceResponseWrapper.go → src/presentation/api/helper/liaisonResponseWrapper.go

@@ -3,7 +3,7 @@ package apiHelper
 import (
 	"net/http"
 
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/labstack/echo/v4"
 )
 
@@ -12,25 +12,25 @@ type newFormattedResponse struct {
 	Body   interface{} `json:"body"`
 }
 
-func ServiceResponseWrapper(
+func LiaisonResponseWrapper(
 	c echo.Context,
-	serviceOutput service.ServiceOutput,
+	liaisonOutput liaison.LiaisonOutput,
 ) error {
 	responseStatus := http.StatusOK
-	switch serviceOutput.Status {
-	case service.Created:
+	switch liaisonOutput.Status {
+	case liaison.Created:
 		responseStatus = http.StatusCreated
-	case service.MultiStatus:
+	case liaison.MultiStatus:
 		responseStatus = http.StatusMultiStatus
-	case service.UserError:
+	case liaison.UserError:
 		responseStatus = http.StatusBadRequest
-	case service.InfraError:
+	case liaison.InfraError:
 		responseStatus = http.StatusInternalServerError
 	}
 
 	formattedResponse := newFormattedResponse{
 		Status: responseStatus,
-		Body:   serviceOutput.Body,
+		Body:   liaisonOutput.Body,
 	}
 	return c.JSON(responseStatus, formattedResponse)
 }

+ 1 - 1
src/presentation/api/helper/readRequestInputData.go

@@ -113,7 +113,7 @@ func ReadRequestInputData(c echo.Context) (map[string]interface{}, error) {
 		requestBody[paramName] = c.Param(paramName)
 	}
 
-	requestBody["operatorAccountId"] = c.Get("accountId")
+	requestBody["operatorAccountId"] = c.Get("operatorAccountId")
 	requestBody["operatorIpAddress"] = c.RealIP()
 
 	return requestBody, nil

+ 1 - 1
src/presentation/api/middleware/authentication.go

@@ -94,7 +94,7 @@ func Authentication(apiBasePath string) echo.MiddlewareFunc {
 				return authError("InvalidAccessToken")
 			}
 
-			c.Set("accountId", accountId.String())
+			c.Set("operatorAccountId", accountId.String())
 			return next(c)
 		}
 	}

+ 17 - 0
src/presentation/api/router.go

@@ -4,6 +4,8 @@ import (
 	_ "embed"
 	"net/http"
 	"net/url"
+	"os"
+	"strconv"
 	"strings"
 
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
@@ -144,6 +146,21 @@ func (router Router) runtimeRoutes() {
 
 	runtimeGroup.GET("/php/:hostname/", runtimeController.ReadPhpConfigs)
 	runtimeGroup.PUT("/php/:hostname/", runtimeController.UpdatePhpConfigs)
+
+	rawEnableApiRuntimePhpRunCmdEnvVar := os.Getenv("ENABLE_API_RUNTIME_PHP_RUN_CMD")
+	if rawEnableApiRuntimePhpRunCmdEnvVar == "" {
+		return
+	}
+
+	shouldEnablePhpRunCmd, err := strconv.ParseBool(rawEnableApiRuntimePhpRunCmdEnvVar)
+	if err != nil {
+		return
+	}
+
+	if shouldEnablePhpRunCmd {
+		runtimeGroup.POST("/php/:hostname/run/", runtimeController.RunPhpCommand)
+		runtimeGroup.POST("/php/run/", runtimeController.RunPhpCommand)
+	}
 }
 
 func (router *Router) scheduledTaskRoutes() {

+ 15 - 15
src/presentation/cli/controller/account.go

@@ -3,12 +3,12 @@ package cliController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/spf13/cobra"
 )
 
 type AccountController struct {
-	accountService *service.AccountService
+	accountLiaison *liaison.AccountLiaison
 }
 
 func NewAccountController(
@@ -16,7 +16,7 @@ func NewAccountController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *AccountController {
 	return &AccountController{
-		accountService: service.NewAccountService(persistentDbSvc, trailDbSvc),
+		accountLiaison: liaison.NewAccountLiaison(persistentDbSvc, trailDbSvc),
 	}
 }
 
@@ -63,8 +63,8 @@ func (controller *AccountController) Read() *cobra.Command {
 				requestBody["lastSeenId"] = paginationLastSeenIdStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.accountService.Read(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.accountLiaison.Read(requestBody),
 			)
 		},
 	}
@@ -112,8 +112,8 @@ func (controller *AccountController) Create() *cobra.Command {
 				"isSuperAdmin": isSuperAdminStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.accountService.Create(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.accountLiaison.Create(requestBody),
 			)
 		},
 	}
@@ -156,8 +156,8 @@ func (controller *AccountController) Update() *cobra.Command {
 				requestBody["isSuperAdmin"] = isSuperAdminStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.accountService.Update(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.accountLiaison.Update(requestBody),
 			)
 		},
 	}
@@ -183,8 +183,8 @@ func (controller *AccountController) Delete() *cobra.Command {
 				"accountId": accountIdUint64,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.accountService.Delete(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.accountLiaison.Delete(requestBody),
 			)
 		},
 	}
@@ -211,8 +211,8 @@ func (controller *AccountController) CreateSecureAccessPublicKey() *cobra.Comman
 				requestBody["name"] = keyNameStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.accountService.CreateSecureAccessPublicKey(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.accountLiaison.CreateSecureAccessPublicKey(requestBody),
 			)
 		},
 	}
@@ -242,8 +242,8 @@ func (controller *AccountController) DeleteSecureAccessPublicKey() *cobra.Comman
 				"id":        keyIdUint16,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.accountService.DeleteSecureAccessPublicKey(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.accountLiaison.DeleteSecureAccessPublicKey(requestBody),
 			)
 		},
 	}

+ 5 - 5
src/presentation/cli/controller/authentication.go

@@ -3,12 +3,12 @@ package cliController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/spf13/cobra"
 )
 
 type AuthenticationController struct {
-	authenticationService *service.AuthenticationService
+	authenticationLiaison *liaison.AuthenticationLiaison
 }
 
 func NewAuthenticationController(
@@ -16,7 +16,7 @@ func NewAuthenticationController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *AuthenticationController {
 	return &AuthenticationController{
-		authenticationService: service.NewAuthenticationService(
+		authenticationLiaison: liaison.NewAuthenticationLiaison(
 			persistentDbSvc, trailDbSvc,
 		),
 	}
@@ -35,8 +35,8 @@ func (controller *AuthenticationController) Login() *cobra.Command {
 				"operatorIpAddress": ipAddressStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.authenticationService.Login(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.authenticationLiaison.Login(requestBody),
 			)
 		},
 	}

+ 10 - 10
src/presentation/cli/controller/cron.go

@@ -3,19 +3,19 @@ package cliController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/spf13/cobra"
 )
 
 type CronController struct {
-	cronService *service.CronService
+	cronLiaison *liaison.CronLiaison
 }
 
 func NewCronController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *CronController {
 	return &CronController{
-		cronService: service.NewCronService(trailDbSvc),
+		cronLiaison: liaison.NewCronLiaison(trailDbSvc),
 	}
 }
 
@@ -60,7 +60,7 @@ func (controller *CronController) Read() *cobra.Command {
 				requestBody["lastSeenId"] = paginationLastSeenIdStr
 			}
 
-			cliHelper.ServiceResponseWrapper(controller.cronService.Read(requestBody))
+			cliHelper.LiaisonResponseWrapper(controller.cronLiaison.Read(requestBody))
 		},
 	}
 
@@ -103,8 +103,8 @@ func (controller *CronController) Create() *cobra.Command {
 				requestBody["comment"] = commentStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.cronService.Create(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.cronLiaison.Create(requestBody),
 			)
 		},
 	}
@@ -140,8 +140,8 @@ func (controller *CronController) Update() *cobra.Command {
 				requestBody["comment"] = commentStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.cronService.Update(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.cronLiaison.Update(requestBody),
 			)
 		},
 	}
@@ -171,8 +171,8 @@ func (controller *CronController) Delete() *cobra.Command {
 				requestBody["comment"] = commentStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.cronService.Delete(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.cronLiaison.Delete(requestBody),
 			)
 		},
 	}

+ 13 - 13
src/presentation/cli/controller/database.go

@@ -4,14 +4,14 @@ import (
 	"github.com/goinfinite/os/src/domain/valueObject"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	tkPresentation "github.com/goinfinite/tk/src/presentation"
 	"github.com/spf13/cobra"
 )
 
 type DatabaseController struct {
 	persistentDbService *internalDbInfra.PersistentDatabaseService
-	dbService           *service.DatabaseService
+	databaseLiaison     *liaison.DatabaseLiaison
 }
 
 func NewDatabaseController(
@@ -20,7 +20,7 @@ func NewDatabaseController(
 ) *DatabaseController {
 	return &DatabaseController{
 		persistentDbService: persistentDbService,
-		dbService: service.NewDatabaseService(
+		databaseLiaison: liaison.NewDatabaseLiaison(
 			persistentDbService, trailDbSvc,
 		),
 	}
@@ -55,8 +55,8 @@ func (controller *DatabaseController) Read() *cobra.Command {
 				paginationSortByStr, paginationSortDirectionStr, paginationLastSeenIdStr,
 			)
 
-			cliHelper.ServiceResponseWrapper(
-				controller.dbService.Read(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.databaseLiaison.Read(requestBody),
 			)
 		},
 	}
@@ -95,8 +95,8 @@ func (controller *DatabaseController) Create() *cobra.Command {
 				"dbName": dbNameStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.dbService.Create(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.databaseLiaison.Create(requestBody),
 			)
 		},
 	}
@@ -120,8 +120,8 @@ func (controller *DatabaseController) Delete() *cobra.Command {
 				"dbName": dbNameStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.dbService.Delete(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.databaseLiaison.Delete(requestBody),
 			)
 		},
 	}
@@ -154,8 +154,8 @@ func (controller *DatabaseController) CreateUser() *cobra.Command {
 				)
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.dbService.CreateUser(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.databaseLiaison.CreateUser(requestBody),
 			)
 		},
 	}
@@ -189,8 +189,8 @@ func (controller *DatabaseController) DeleteUser() *cobra.Command {
 				"dbUser": dbUsernameStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.dbService.DeleteUser(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.databaseLiaison.DeleteUser(requestBody),
 			)
 		},
 	}

+ 11 - 11
src/presentation/cli/controller/marketplace.go

@@ -7,13 +7,13 @@ import (
 	"github.com/goinfinite/os/src/domain/valueObject"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/spf13/cobra"
 )
 
 type MarketplaceController struct {
 	persistentDbSvc    *internalDbInfra.PersistentDatabaseService
-	marketplaceService *service.MarketplaceService
+	marketplaceLiaison *liaison.MarketplaceLiaison
 }
 
 func NewMarketplaceController(
@@ -22,7 +22,7 @@ func NewMarketplaceController(
 ) *MarketplaceController {
 	return &MarketplaceController{
 		persistentDbSvc:    persistentDbSvc,
-		marketplaceService: service.NewMarketplaceService(persistentDbSvc, trailDbSvc),
+		marketplaceLiaison: liaison.NewMarketplaceLiaison(persistentDbSvc, trailDbSvc),
 	}
 }
 
@@ -75,8 +75,8 @@ func (controller *MarketplaceController) ReadCatalog() *cobra.Command {
 				requestBody["lastSeenId"] = paginationLastSeenIdStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.marketplaceService.ReadCatalog(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.marketplaceLiaison.ReadCatalog(requestBody),
 			)
 		},
 	}
@@ -176,8 +176,8 @@ func (controller *MarketplaceController) InstallCatalogItem() *cobra.Command {
 				requestBody["urlPath"] = urlPathStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.marketplaceService.InstallCatalogItem(requestBody, false),
+			cliHelper.LiaisonResponseWrapper(
+				controller.marketplaceLiaison.InstallCatalogItem(requestBody, false),
 			)
 		},
 	}
@@ -242,8 +242,8 @@ func (controller *MarketplaceController) ReadInstalledItems() *cobra.Command {
 				requestBody["lastSeenId"] = paginationLastSeenIdStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.marketplaceService.ReadInstalledItems(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.marketplaceLiaison.ReadInstalledItems(requestBody),
 			)
 		},
 	}
@@ -295,8 +295,8 @@ func (controller *MarketplaceController) DeleteInstalledItem() *cobra.Command {
 				"shouldUninstallServices": shouldUninstallServicesStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.marketplaceService.DeleteInstalledItem(requestBody, false),
+			cliHelper.LiaisonResponseWrapper(
+				controller.marketplaceLiaison.DeleteInstalledItem(requestBody, false),
 			)
 		},
 	}

+ 4 - 4
src/presentation/cli/controller/o11y.go

@@ -3,19 +3,19 @@ package cliController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/spf13/cobra"
 )
 
 type O11yController struct {
-	o11yService *service.O11yService
+	o11yLiaison *liaison.O11yLiaison
 }
 
 func NewO11yController(
 	transientDbService *internalDbInfra.TransientDatabaseService,
 ) *O11yController {
 	return &O11yController{
-		o11yService: service.NewO11yService(transientDbService),
+		o11yLiaison: liaison.NewO11yLiaison(transientDbService),
 	}
 }
 
@@ -24,7 +24,7 @@ func (controller *O11yController) ReadOverview() *cobra.Command {
 		Use:   "overview",
 		Short: "ReadO11yOverview",
 		Run: func(cmd *cobra.Command, args []string) {
-			cliHelper.ServiceResponseWrapper(controller.o11yService.ReadOverview())
+			cliHelper.LiaisonResponseWrapper(controller.o11yLiaison.ReadOverview())
 		},
 	}
 

+ 40 - 11
src/presentation/cli/controller/runtime.go

@@ -8,12 +8,12 @@ import (
 	infraHelper "github.com/goinfinite/os/src/infra/helper"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/spf13/cobra"
 )
 
 type RuntimeController struct {
-	runtimeService *service.RuntimeService
+	runtimeLiaison *liaison.RuntimeLiaison
 }
 
 func NewRuntimeController(
@@ -21,7 +21,7 @@ func NewRuntimeController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *RuntimeController {
 	return &RuntimeController{
-		runtimeService: service.NewRuntimeService(persistentDbService, trailDbSvc),
+		runtimeLiaison: liaison.NewRuntimeLiaison(persistentDbService, trailDbSvc),
 	}
 }
 
@@ -55,8 +55,8 @@ func (controller *RuntimeController) ReadPhpConfigs() *cobra.Command {
 				"hostname": hostname.String(),
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.runtimeService.ReadPhpConfigs(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.runtimeLiaison.ReadPhpConfigs(requestBody),
 			)
 		},
 	}
@@ -107,8 +107,8 @@ func (controller *RuntimeController) UpdatePhpConfig() *cobra.Command {
 				requestBody["settings"] = settings
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.runtimeService.UpdatePhpConfigs(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.runtimeLiaison.UpdatePhpConfigs(requestBody),
 			)
 		},
 	}
@@ -148,8 +148,8 @@ func (controller *RuntimeController) UpdatePhpModule() *cobra.Command {
 			}
 			requestBody["modules"] = []entity.PhpModule{module}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.runtimeService.UpdatePhpConfigs(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.runtimeLiaison.UpdatePhpConfigs(requestBody),
 			)
 		},
 	}
@@ -187,8 +187,8 @@ func (controller *RuntimeController) UpdatePhpSetting() *cobra.Command {
 			}
 			requestBody["settings"] = []entity.PhpSetting{setting}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.runtimeService.UpdatePhpConfigs(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.runtimeLiaison.UpdatePhpConfigs(requestBody),
 			)
 		},
 	}
@@ -202,3 +202,32 @@ func (controller *RuntimeController) UpdatePhpSetting() *cobra.Command {
 	cmd.MarkFlagRequired("value")
 	return cmd
 }
+
+func (controller *RuntimeController) RunPhpCommand() *cobra.Command {
+	var hostnameStr, commandStr string
+
+	cmd := &cobra.Command{
+		Use:   "run",
+		Short: "RunPhpCommand",
+		Run: func(cmd *cobra.Command, args []string) {
+			hostname, err := getHostname(hostnameStr)
+			if err != nil {
+				cliHelper.ResponseWrapper(false, err.Error())
+			}
+			requestBody := map[string]interface{}{
+				"hostname": hostname.String(),
+				"command":  commandStr,
+			}
+
+			cliHelper.LiaisonResponseWrapper(
+				controller.runtimeLiaison.RunPhpCommand(requestBody),
+			)
+		},
+	}
+
+	cmd.Flags().StringVarP(&hostnameStr, "hostname", "n", "", "Hostname")
+	cmd.MarkFlagRequired("hostname")
+	cmd.Flags().StringVarP(&commandStr, "command", "c", "", "Command")
+	cmd.MarkFlagRequired("command")
+	return cmd
+}

+ 7 - 7
src/presentation/cli/controller/scheduledTask.go

@@ -3,13 +3,13 @@ package cliController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"github.com/spf13/cobra"
 )
 
 type ScheduledTaskController struct {
 	persistentDbSvc      *internalDbInfra.PersistentDatabaseService
-	scheduledTaskService *service.ScheduledTaskService
+	scheduledTaskLiaison *liaison.ScheduledTaskLiaison
 }
 
 func NewScheduledTaskController(
@@ -17,7 +17,7 @@ func NewScheduledTaskController(
 ) *ScheduledTaskController {
 	return &ScheduledTaskController{
 		persistentDbSvc:      persistentDbSvc,
-		scheduledTaskService: service.NewScheduledTaskService(persistentDbSvc),
+		scheduledTaskLiaison: liaison.NewScheduledTaskLiaison(persistentDbSvc),
 	}
 }
 
@@ -85,8 +85,8 @@ func (controller *ScheduledTaskController) Read() *cobra.Command {
 				requestBody["lastSeenId"] = paginationLastSeenIdStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.scheduledTaskService.Read(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.scheduledTaskLiaison.Read(requestBody),
 			)
 		},
 	}
@@ -152,8 +152,8 @@ func (controller *ScheduledTaskController) Update() *cobra.Command {
 				requestBody["runAt"] = runAtInt64
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.scheduledTaskService.Update(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.scheduledTaskLiaison.Update(requestBody),
 			)
 		},
 	}

+ 15 - 15
src/presentation/cli/controller/services.go

@@ -4,13 +4,13 @@ import (
 	"github.com/goinfinite/os/src/domain/valueObject"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	sharedHelper "github.com/goinfinite/os/src/presentation/shared/helper"
 	"github.com/spf13/cobra"
 )
 
 type ServicesController struct {
-	servicesService *service.ServicesService
+	servicesLiaison *liaison.ServicesLiaison
 }
 
 func NewServicesController(
@@ -18,7 +18,7 @@ func NewServicesController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *ServicesController {
 	return &ServicesController{
-		servicesService: service.NewServicesService(persistentDbSvc, trailDbSvc),
+		servicesLiaison: liaison.NewServicesLiaison(persistentDbSvc, trailDbSvc),
 	}
 }
 
@@ -69,8 +69,8 @@ func (controller *ServicesController) ReadInstalledItems() *cobra.Command {
 				requestBody["lastSeenId"] = paginationLastSeenIdStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.servicesService.ReadInstalledItems(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.servicesLiaison.ReadInstalledItems(requestBody),
 			)
 		},
 	}
@@ -154,8 +154,8 @@ func (controller *ServicesController) ReadInstallableItems() *cobra.Command {
 				requestBody["lastSeenId"] = paginationLastSeenIdStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.servicesService.ReadInstallableItems(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.servicesLiaison.ReadInstallableItems(requestBody),
 			)
 		},
 	}
@@ -244,8 +244,8 @@ func (controller *ServicesController) CreateInstallable() *cobra.Command {
 				requestBody["mappingPath"] = mappingPath
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.servicesService.CreateInstallable(requestBody, false),
+			cliHelper.LiaisonResponseWrapper(
+				controller.servicesLiaison.CreateInstallable(requestBody, false),
 			)
 		},
 	}
@@ -341,8 +341,8 @@ func (controller *ServicesController) CreateCustom() *cobra.Command {
 				requestBody["mappingPath"] = mappingPath
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.servicesService.CreateCustom(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.servicesLiaison.CreateCustom(requestBody),
 			)
 		},
 	}
@@ -428,8 +428,8 @@ func (controller *ServicesController) Update() *cobra.Command {
 				requestBody["portBindings"] = portBindingsSlice
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.servicesService.Update(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.servicesLiaison.Update(requestBody),
 			)
 		},
 	}
@@ -459,8 +459,8 @@ func (controller *ServicesController) Delete() *cobra.Command {
 				"name": nameStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.servicesService.Delete(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.servicesLiaison.Delete(requestBody),
 			)
 		},
 	}

+ 9 - 9
src/presentation/cli/controller/ssl.go

@@ -5,13 +5,13 @@ import (
 	infraHelper "github.com/goinfinite/os/src/infra/helper"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	tkPresentation "github.com/goinfinite/tk/src/presentation"
 	"github.com/spf13/cobra"
 )
 
 type SslController struct {
-	sslService *service.SslService
+	sslLiaison *liaison.SslLiaison
 }
 
 func NewSslController(
@@ -20,7 +20,7 @@ func NewSslController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *SslController {
 	return &SslController{
-		sslService: service.NewSslService(persistentDbSvc, transientDbSvc, trailDbSvc),
+		sslLiaison: liaison.NewSslLiaison(persistentDbSvc, transientDbSvc, trailDbSvc),
 	}
 }
 
@@ -51,8 +51,8 @@ func (controller *SslController) Read() *cobra.Command {
 				paginationSortByStr, paginationSortDirectionStr, paginationLastSeenIdStr,
 			)
 
-			cliHelper.ServiceResponseWrapper(
-				controller.sslService.Read(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.sslLiaison.Read(requestBody),
 			)
 		},
 	}
@@ -119,7 +119,7 @@ func (controller *SslController) Create() *cobra.Command {
 			}
 			requestBody["key"] = privateKeyContentStr
 
-			cliHelper.ServiceResponseWrapper(controller.sslService.Create(requestBody))
+			cliHelper.LiaisonResponseWrapper(controller.sslLiaison.Create(requestBody))
 		},
 	}
 
@@ -147,8 +147,8 @@ func (controller *SslController) CreatePubliclyTrusted() *cobra.Command {
 				"virtualHostHostname": hostnameStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.sslService.CreatePubliclyTrusted(requestBody, false),
+			cliHelper.LiaisonResponseWrapper(
+				controller.sslLiaison.CreatePubliclyTrusted(requestBody, false),
 			)
 		},
 	}
@@ -169,7 +169,7 @@ func (controller *SslController) Delete() *cobra.Command {
 				"id": sslPairIdStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(controller.sslService.Delete(requestBody))
+			cliHelper.LiaisonResponseWrapper(controller.sslLiaison.Delete(requestBody))
 		},
 	}
 

+ 27 - 27
src/presentation/cli/controller/virtualHost.go

@@ -3,14 +3,14 @@ package cliController
 import (
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	cliHelper "github.com/goinfinite/os/src/presentation/cli/helper"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	tkValueObject "github.com/goinfinite/tk/src/domain/valueObject"
 	tkPresentation "github.com/goinfinite/tk/src/presentation"
 	"github.com/spf13/cobra"
 )
 
 type VirtualHostController struct {
-	virtualHostService *service.VirtualHostService
+	virtualHostLiaison *liaison.VirtualHostLiaison
 }
 
 func NewVirtualHostController(
@@ -18,7 +18,7 @@ func NewVirtualHostController(
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
 ) *VirtualHostController {
 	return &VirtualHostController{
-		virtualHostService: service.NewVirtualHostService(persistentDbSvc, trailDbSvc),
+		virtualHostLiaison: liaison.NewVirtualHostLiaison(persistentDbSvc, trailDbSvc),
 	}
 }
 
@@ -55,8 +55,8 @@ func (controller *VirtualHostController) Read() *cobra.Command {
 				paginationSortByStr, paginationSortDirectionStr, paginationLastSeenIdStr,
 			)
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.Read(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.Read(requestBody),
 			)
 		},
 	}
@@ -106,8 +106,8 @@ func (controller *VirtualHostController) Create() *cobra.Command {
 				requestBody["parentHostname"] = parentHostnameStr
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.Create(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.Create(requestBody),
 			)
 		},
 	}
@@ -136,8 +136,8 @@ func (controller *VirtualHostController) Update() *cobra.Command {
 				"isWildcard": isWildcardBoolStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.Update(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.Update(requestBody),
 			)
 		},
 	}
@@ -161,8 +161,8 @@ func (controller *VirtualHostController) Delete() *cobra.Command {
 				"hostname": hostnameStr,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.Delete(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.Delete(requestBody),
 			)
 		},
 	}
@@ -204,8 +204,8 @@ func (controller *VirtualHostController) ReadWithMappings() *cobra.Command {
 				paginationSortByStr, paginationSortDirectionStr, paginationLastSeenIdStr,
 			)
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.Read(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.Read(requestBody),
 			)
 		},
 	}
@@ -266,8 +266,8 @@ func (controller *VirtualHostController) CreateMapping() *cobra.Command {
 				requestBody["mappingSecurityRuleId"] = mappingSecurityRuleIdUint
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.CreateMapping(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.CreateMapping(requestBody),
 			)
 		},
 	}
@@ -343,8 +343,8 @@ func (controller *VirtualHostController) UpdateMapping() *cobra.Command {
 				requestBody["mappingSecurityRuleId"] = mappingSecurityRuleIdUint
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.UpdateMapping(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.UpdateMapping(requestBody),
 			)
 		},
 	}
@@ -385,8 +385,8 @@ func (controller *VirtualHostController) DeleteMapping() *cobra.Command {
 				"id": mappingIdUint,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.DeleteMapping(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.DeleteMapping(requestBody),
 			)
 		},
 	}
@@ -434,8 +434,8 @@ func (controller *VirtualHostController) ReadMappingSecurityRules() *cobra.Comma
 				paginationSortByStr, paginationSortDirectionStr, paginationLastSeenIdStr,
 			)
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.ReadMappingSecurityRules(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.ReadMappingSecurityRules(requestBody),
 			)
 		},
 	}
@@ -528,8 +528,8 @@ func (controller *VirtualHostController) CreateMappingSecurityRule() *cobra.Comm
 				requestBody["responseCodeOnMaxConnections"] = responseCodeOnMaxConnectionsUint
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.CreateMappingSecurityRule(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.CreateMappingSecurityRule(requestBody),
 			)
 		},
 	}
@@ -640,8 +640,8 @@ func (controller *VirtualHostController) UpdateMappingSecurityRule() *cobra.Comm
 				requestBody["responseCodeOnMaxConnections"] = responseCodeOnMaxConnectionsUint
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.UpdateMappingSecurityRule(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.UpdateMappingSecurityRule(requestBody),
 			)
 		},
 	}
@@ -699,8 +699,8 @@ func (controller *VirtualHostController) DeleteMappingSecurityRule() *cobra.Comm
 				"id": ruleIdUint,
 			}
 
-			cliHelper.ServiceResponseWrapper(
-				controller.virtualHostService.DeleteMappingSecurityRule(requestBody),
+			cliHelper.LiaisonResponseWrapper(
+				controller.virtualHostLiaison.DeleteMappingSecurityRule(requestBody),
 			)
 		},
 	}

+ 8 - 8
src/presentation/cli/helper/serviceResponseWrapper.go → src/presentation/cli/helper/liaisonResponseWrapper.go

@@ -8,25 +8,25 @@ import (
 	"github.com/alecthomas/chroma/formatters"
 	"github.com/alecthomas/chroma/lexers"
 	"github.com/alecthomas/chroma/styles"
-	"github.com/goinfinite/os/src/presentation/service"
+	"github.com/goinfinite/os/src/presentation/liaison"
 	"golang.org/x/term"
 )
 
-func ServiceResponseWrapper(serviceOutput service.ServiceOutput) {
+func LiaisonResponseWrapper(liaisonOutput liaison.LiaisonOutput) {
 	exitCode := 0
-	switch serviceOutput.Status {
-	case service.MultiStatus:
+	switch liaisonOutput.Status {
+	case liaison.MultiStatus:
 		exitCode = 1
-	case service.UserError:
+	case liaison.UserError:
 		exitCode = 1
-	case service.InfraError:
+	case liaison.InfraError:
 		exitCode = 1
 	}
 
 	stdoutFileDescriptor := int(os.Stdout.Fd())
 	isNonInteractiveSession := !term.IsTerminal(stdoutFileDescriptor)
 	if isNonInteractiveSession {
-		standardJsonBytes, err := json.Marshal(serviceOutput)
+		standardJsonBytes, err := json.Marshal(liaisonOutput)
 		if err != nil {
 			fmt.Println("ResponseEncodingError")
 			os.Exit(1)
@@ -36,7 +36,7 @@ func ServiceResponseWrapper(serviceOutput service.ServiceOutput) {
 		os.Exit(exitCode)
 	}
 
-	prettyJsonBytes, err := json.MarshalIndent(serviceOutput, "", "  ")
+	prettyJsonBytes, err := json.MarshalIndent(liaisonOutput, "", "  ")
 	if err != nil {
 		fmt.Println("PrettyResponseEncodingError")
 		os.Exit(1)

+ 1 - 0
src/presentation/cli/router.go

@@ -147,6 +147,7 @@ func (router Router) runtimeRoutes() {
 	phpCmd.AddCommand(runtimeController.UpdatePhpConfig())
 	phpCmd.AddCommand(runtimeController.UpdatePhpSetting())
 	phpCmd.AddCommand(runtimeController.UpdatePhpModule())
+	phpCmd.AddCommand(runtimeController.RunPhpCommand())
 }
 
 func (router *Router) scheduledTaskRoutes() {

+ 419 - 0
src/presentation/liaison/account.go

@@ -0,0 +1,419 @@
+package liaison
+
+import (
+	"errors"
+
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/useCase"
+	"github.com/goinfinite/os/src/domain/valueObject"
+	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
+	accountInfra "github.com/goinfinite/os/src/infra/account"
+	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
+	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
+)
+
+var LocalOperatorAccountId = valueObject.AccountIdSystem
+var LocalOperatorIpAddress = valueObject.IpAddressSystem
+
+type AccountLiaison struct {
+	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
+	accountQueryRepo      *accountInfra.AccountQueryRepo
+	accountCmdRepo        *accountInfra.AccountCmdRepo
+	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
+}
+
+func NewAccountLiaison(
+	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
+	trailDbSvc *internalDbInfra.TrailDatabaseService,
+) *AccountLiaison {
+	return &AccountLiaison{
+		persistentDbSvc:       persistentDbSvc,
+		accountQueryRepo:      accountInfra.NewAccountQueryRepo(persistentDbSvc),
+		accountCmdRepo:        accountInfra.NewAccountCmdRepo(persistentDbSvc),
+		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
+	}
+}
+
+func (liaison *AccountLiaison) Read(untrustedInput map[string]any) LiaisonOutput {
+	if untrustedInput["id"] != nil && untrustedInput["accountId"] == nil {
+		untrustedInput["accountId"] = untrustedInput["id"]
+	}
+
+	var idPtr *valueObject.AccountId
+	if untrustedInput["accountId"] != nil {
+		id, err := valueObject.NewAccountId(untrustedInput["accountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		idPtr = &id
+	}
+
+	var usernamePtr *valueObject.Username
+	if untrustedInput["username"] != nil {
+		username, err := valueObject.NewUsername(untrustedInput["username"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		usernamePtr = &username
+	}
+
+	shouldIncludeSecureAccessPublicKeys := false
+	if untrustedInput["shouldIncludeSecureAccessPublicKeys"] != nil {
+		var err error
+		shouldIncludeSecureAccessPublicKeys, err = voHelper.InterfaceToBool(
+			untrustedInput["shouldIncludeSecureAccessPublicKeys"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+	}
+
+	paginationDto := useCase.AccountsDefaultPagination
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidPageNumber"))
+		}
+		paginationDto.PageNumber = pageNumber
+	}
+
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidItemsPerPage"))
+		}
+		paginationDto.ItemsPerPage = itemsPerPage
+	}
+
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortBy = &sortBy
+	}
+
+	if untrustedInput["sortDirection"] != nil {
+		sortDirection, err := valueObject.NewPaginationSortDirection(
+			untrustedInput["sortDirection"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortDirection = &sortDirection
+	}
+
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.LastSeenId = &lastSeenId
+	}
+
+	readRequestDto := dto.ReadAccountsRequest{
+		Pagination:                          paginationDto,
+		AccountId:                           idPtr,
+		AccountUsername:                     usernamePtr,
+		ShouldIncludeSecureAccessPublicKeys: &shouldIncludeSecureAccessPublicKeys,
+	}
+
+	accountsList, err := useCase.ReadAccounts(liaison.accountQueryRepo, readRequestDto)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, accountsList)
+}
+
+func (liaison *AccountLiaison) Create(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"username", "password"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	username, err := valueObject.NewUsername(untrustedInput["username"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	password, err := valueObject.NewPassword(untrustedInput["password"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	isSuperAdmin := false
+	if untrustedInput["isSuperAdmin"] != nil {
+		isSuperAdmin, err = voHelper.InterfaceToBool(untrustedInput["isSuperAdmin"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateAccount(
+		username, password, isSuperAdmin, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.CreateAccount(
+		liaison.accountQueryRepo, liaison.accountCmdRepo,
+		liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "AccountCreated")
+}
+
+func (liaison *AccountLiaison) Update(untrustedInput map[string]any) LiaisonOutput {
+	if untrustedInput["id"] != nil {
+		untrustedInput["accountId"] = untrustedInput["id"]
+	}
+
+	requiredParams := []string{"accountId"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	accountId, err := valueObject.NewAccountId(untrustedInput["accountId"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var passwordPtr *valueObject.Password
+	if untrustedInput["password"] != nil && untrustedInput["password"] != "" {
+		password, err := valueObject.NewPassword(untrustedInput["password"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		passwordPtr = &password
+	}
+
+	var isSuperAdminPtr *bool
+	if untrustedInput["isSuperAdmin"] != nil {
+		isSuperAdmin, err := voHelper.InterfaceToBool(untrustedInput["isSuperAdmin"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		isSuperAdminPtr = &isSuperAdmin
+	}
+
+	var shouldUpdateApiKeyPtr *bool
+	if untrustedInput["shouldUpdateApiKey"] != nil {
+		shouldUpdateApiKey, err := voHelper.InterfaceToBool(untrustedInput["shouldUpdateApiKey"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		shouldUpdateApiKeyPtr = &shouldUpdateApiKey
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	updateDto := dto.NewUpdateAccount(
+		accountId, passwordPtr, isSuperAdminPtr, shouldUpdateApiKeyPtr,
+		operatorAccountId, operatorIpAddress,
+	)
+
+	if updateDto.ShouldUpdateApiKey != nil && *updateDto.ShouldUpdateApiKey {
+		newKey, err := useCase.UpdateAccountApiKey(
+			liaison.accountQueryRepo, liaison.accountCmdRepo,
+			liaison.activityRecordCmdRepo, updateDto,
+		)
+		if err != nil {
+			return NewLiaisonOutput(InfraError, err.Error())
+		}
+		return NewLiaisonOutput(Success, newKey)
+	}
+
+	err = useCase.UpdateAccount(
+		liaison.accountQueryRepo, liaison.accountCmdRepo,
+		liaison.activityRecordCmdRepo, updateDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "AccountUpdated")
+}
+
+func (liaison *AccountLiaison) Delete(untrustedInput map[string]any) LiaisonOutput {
+	if untrustedInput["id"] != nil && untrustedInput["accountId"] == nil {
+		untrustedInput["accountId"] = untrustedInput["id"]
+	}
+
+	requiredParams := []string{"accountId"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	accountId, err := valueObject.NewAccountId(untrustedInput["accountId"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteAccount(accountId, operatorAccountId, operatorIpAddress)
+
+	err = useCase.DeleteAccount(
+		liaison.accountQueryRepo, liaison.accountCmdRepo,
+		liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "AccountDeleted")
+}
+
+func (liaison *AccountLiaison) CreateSecureAccessPublicKey(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"accountId", "content"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	keyContent, err := valueObject.NewSecureAccessPublicKeyContent(untrustedInput["content"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	keyName, err := valueObject.NewSecureAccessPublicKeyName(untrustedInput["name"])
+	if err != nil {
+		keyName, err = keyContent.ReadOnlyKeyName()
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	accountId, err := valueObject.NewAccountId(untrustedInput["accountId"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateSecureAccessPublicKey(
+		accountId, keyContent, keyName, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.CreateSecureAccessPublicKey(
+		liaison.accountCmdRepo, liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "SecureAccessPublicKeyCreated")
+}
+
+func (liaison *AccountLiaison) DeleteSecureAccessPublicKey(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"secureAccessPublicKeyId"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	keyId, err := valueObject.NewSecureAccessPublicKeyId(
+		untrustedInput["secureAccessPublicKeyId"],
+	)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteSecureAccessPublicKey(
+		keyId, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.DeleteSecureAccessPublicKey(
+		liaison.accountQueryRepo, liaison.accountCmdRepo,
+		liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "SecureAccessPublicKeyDeleted")
+}

+ 24 - 22
src/presentation/service/authentication.go → src/presentation/liaison/authentication.go

@@ -1,4 +1,4 @@
-package service
+package liaison
 
 import (
 	"github.com/goinfinite/os/src/domain/dto"
@@ -8,56 +8,58 @@ import (
 	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
 	authInfra "github.com/goinfinite/os/src/infra/auth"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
 )
 
-type AuthenticationService struct {
+type AuthenticationLiaison struct {
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService
 	trailDbSvc      *internalDbInfra.TrailDatabaseService
 }
 
-func NewAuthenticationService(
+func NewAuthenticationLiaison(
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *AuthenticationService {
-	return &AuthenticationService{
+) *AuthenticationLiaison {
+	return &AuthenticationLiaison{
 		persistentDbSvc: persistentDbSvc,
 		trailDbSvc:      trailDbSvc,
 	}
 }
 
-func (service *AuthenticationService) Login(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"username", "password"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
+func (liaison *AuthenticationLiaison) Login(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"username", "password", "operatorIpAddress"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
-	username, err := valueObject.NewUsername(input["username"])
+	username, err := valueObject.NewUsername(untrustedInput["username"])
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
-	password, err := valueObject.NewPassword(input["password"])
+	password, err := valueObject.NewPassword(untrustedInput["password"])
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
-	operatorIpAddress, err := valueObject.NewIpAddress(input["operatorIpAddress"])
+	operatorIpAddress, err := valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
 	dto := dto.NewCreateSessionToken(username, password, operatorIpAddress)
 
-	authQueryRepo := authInfra.NewAuthQueryRepo(service.persistentDbSvc)
+	authQueryRepo := authInfra.NewAuthQueryRepo(liaison.persistentDbSvc)
 	authCmdRepo := authInfra.AuthCmdRepo{}
-	accountQueryRepo := accountInfra.NewAccountQueryRepo(service.persistentDbSvc)
+	accountQueryRepo := accountInfra.NewAccountQueryRepo(liaison.persistentDbSvc)
 	activityRecordQueryRepo := activityRecordInfra.NewActivityRecordQueryRepo(
-		service.trailDbSvc,
+		liaison.trailDbSvc,
 	)
 	activityRecordCmdRepo := activityRecordInfra.NewActivityRecordCmdRepo(
-		service.trailDbSvc,
+		liaison.trailDbSvc,
 	)
 
 	accessToken, err := useCase.CreateSessionToken(
@@ -65,8 +67,8 @@ func (service *AuthenticationService) Login(input map[string]interface{}) Servic
 		activityRecordCmdRepo, dto,
 	)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Success, accessToken)
+	return NewLiaisonOutput(Success, accessToken)
 }

+ 291 - 0
src/presentation/liaison/cron.go

@@ -0,0 +1,291 @@
+package liaison
+
+import (
+	"errors"
+
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/useCase"
+	"github.com/goinfinite/os/src/domain/valueObject"
+	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
+	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
+	cronInfra "github.com/goinfinite/os/src/infra/cron"
+	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
+)
+
+type CronLiaison struct {
+	cronQueryRepo         *cronInfra.CronQueryRepo
+	cronCmdRepo           *cronInfra.CronCmdRepo
+	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
+}
+
+func NewCronLiaison(
+	trailDbSvc *internalDbInfra.TrailDatabaseService,
+) *CronLiaison {
+	return &CronLiaison{
+		cronQueryRepo:         cronInfra.NewCronQueryRepo(),
+		cronCmdRepo:           cronInfra.NewCronCmdRepo(),
+		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
+	}
+}
+
+func (liaison *CronLiaison) Read(untrustedInput map[string]any) LiaisonOutput {
+	var idPtr *valueObject.CronId
+	if untrustedInput["id"] != nil {
+		id, err := valueObject.NewCronId(untrustedInput["id"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		idPtr = &id
+	}
+
+	var commentPtr *valueObject.CronComment
+	if untrustedInput["comment"] != nil {
+		slug, err := valueObject.NewCronComment(untrustedInput["comment"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		commentPtr = &slug
+	}
+
+	paginationDto := useCase.CronsDefaultPagination
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidPageNumber"))
+		}
+		paginationDto.PageNumber = pageNumber
+	}
+
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidItemsPerPage"))
+		}
+		paginationDto.ItemsPerPage = itemsPerPage
+	}
+
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortBy = &sortBy
+	}
+
+	if untrustedInput["sortDirection"] != nil {
+		sortDirection, err := valueObject.NewPaginationSortDirection(
+			untrustedInput["sortDirection"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortDirection = &sortDirection
+	}
+
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.LastSeenId = &lastSeenId
+	}
+
+	readDto := dto.ReadCronsRequest{
+		Pagination:  paginationDto,
+		CronId:      idPtr,
+		CronComment: commentPtr,
+	}
+
+	cronsList, err := useCase.ReadCrons(liaison.cronQueryRepo, readDto)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, cronsList)
+}
+
+func (liaison *CronLiaison) Create(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"schedule", "command"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	schedule, err := valueObject.NewCronSchedule(untrustedInput["schedule"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	command, err := valueObject.NewUnixCommand(untrustedInput["command"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var commentPtr *valueObject.CronComment
+	if untrustedInput["comment"] != nil && untrustedInput["comment"] != "" {
+		comment, err := valueObject.NewCronComment(untrustedInput["comment"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		commentPtr = &comment
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateCron(
+		schedule, command, commentPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.CreateCron(
+		liaison.cronCmdRepo, liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "CronCreated")
+}
+
+func (liaison *CronLiaison) Update(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"id"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	id, err := valueObject.NewCronId(untrustedInput["id"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var schedulePtr *valueObject.CronSchedule
+	if untrustedInput["schedule"] != nil {
+		schedule, err := valueObject.NewCronSchedule(untrustedInput["schedule"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		schedulePtr = &schedule
+	}
+
+	var commandPtr *valueObject.UnixCommand
+	if untrustedInput["command"] != nil {
+		command, err := valueObject.NewUnixCommand(untrustedInput["command"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		commandPtr = &command
+	}
+
+	clearableFields := []string{}
+
+	var commentPtr *valueObject.CronComment
+	switch commentValue := untrustedInput["comment"]; {
+	case commentValue == nil:
+	case commentValue == "" || commentValue == " ":
+		clearableFields = append(clearableFields, "comment")
+	default:
+		comment, err := valueObject.NewCronComment(commentValue)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		commentPtr = &comment
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	updateDto := dto.NewUpdateCron(
+		id, schedulePtr, commandPtr, commentPtr, clearableFields,
+		operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.UpdateCron(
+		liaison.cronQueryRepo, liaison.cronCmdRepo, liaison.activityRecordCmdRepo,
+		updateDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "CronUpdated")
+}
+
+func (liaison *CronLiaison) Delete(untrustedInput map[string]any) LiaisonOutput {
+	var idPtr *valueObject.CronId
+	if untrustedInput["cronId"] != nil {
+		id, err := valueObject.NewCronId(untrustedInput["cronId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		idPtr = &id
+	}
+
+	var commentPtr *valueObject.CronComment
+	if untrustedInput["comment"] != nil {
+		comment, err := valueObject.NewCronComment(untrustedInput["comment"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		commentPtr = &comment
+	}
+
+	var err error
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteCron(
+		idPtr, commentPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.DeleteCron(
+		liaison.cronQueryRepo, liaison.cronCmdRepo, liaison.activityRecordCmdRepo,
+		deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "CronDeleted")
+}

+ 352 - 0
src/presentation/liaison/database.go

@@ -0,0 +1,352 @@
+package liaison
+
+import (
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/useCase"
+	"github.com/goinfinite/os/src/domain/valueObject"
+	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
+	databaseInfra "github.com/goinfinite/os/src/infra/database"
+	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
+	sharedHelper "github.com/goinfinite/os/src/presentation/shared/helper"
+)
+
+type DatabaseLiaison struct {
+	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
+	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
+	availabilityInspector *sharedHelper.ServiceAvailabilityInspector
+}
+
+func NewDatabaseLiaison(
+	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
+	trailDbSvc *internalDbInfra.TrailDatabaseService,
+) *DatabaseLiaison {
+	return &DatabaseLiaison{
+		persistentDbSvc:       persistentDbSvc,
+		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
+		availabilityInspector: sharedHelper.NewServiceAvailabilityInspector(
+			persistentDbSvc,
+		),
+	}
+}
+
+func (liaison *DatabaseLiaison) Read(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"dbType"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbType, err := valueObject.NewDatabaseType(untrustedInput["dbType"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	serviceName, err := valueObject.NewServiceName(dbType.String())
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+	if !liaison.availabilityInspector.IsAvailable(serviceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	requestPagination, err := liaisonHelper.PaginationParser(
+		untrustedInput, useCase.DatabasesDefaultPagination,
+	)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var databaseNamePtr *valueObject.DatabaseName
+	if untrustedInput["name"] != nil {
+		databaseName, err := valueObject.NewDatabaseName(untrustedInput["name"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		databaseNamePtr = &databaseName
+	}
+
+	var usernamePtr *valueObject.DatabaseUsername
+	if untrustedInput["username"] != nil {
+		username, err := valueObject.NewDatabaseUsername(untrustedInput["username"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		usernamePtr = &username
+	}
+
+	requestDto := dto.ReadDatabasesRequest{
+		Pagination:   requestPagination,
+		DatabaseName: databaseNamePtr,
+		DatabaseType: &dbType,
+		Username:     usernamePtr,
+	}
+
+	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
+
+	responseDto, err := useCase.ReadDatabases(databaseQueryRepo, requestDto)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, responseDto)
+}
+
+func (liaison *DatabaseLiaison) Create(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"dbType", "dbName"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbType, err := valueObject.NewDatabaseType(untrustedInput["dbType"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbName, err := valueObject.NewDatabaseName(untrustedInput["dbName"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	serviceName, err := valueObject.NewServiceName(dbType.String())
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+	if !liaison.availabilityInspector.IsAvailable(serviceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateDatabase(dbName, operatorAccountId, operatorIpAddress)
+
+	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
+	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
+
+	err = useCase.CreateDatabase(
+		databaseQueryRepo, databaseCmdRepo, liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "DatabaseCreated")
+}
+
+func (liaison *DatabaseLiaison) Delete(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"dbType", "dbName"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbType, err := valueObject.NewDatabaseType(untrustedInput["dbType"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	serviceName, err := valueObject.NewServiceName(dbType.String())
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+	if !liaison.availabilityInspector.IsAvailable(serviceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	dbName, err := valueObject.NewDatabaseName(untrustedInput["dbName"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteDatabase(dbName, operatorAccountId, operatorIpAddress)
+
+	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
+	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
+
+	err = useCase.DeleteDatabase(
+		databaseQueryRepo, databaseCmdRepo, liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "DatabaseDeleted")
+}
+
+func (liaison *DatabaseLiaison) CreateUser(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"dbType", "dbName", "username", "password"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbType, err := valueObject.NewDatabaseType(untrustedInput["dbType"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	serviceName, err := valueObject.NewServiceName(dbType.String())
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+	if !liaison.availabilityInspector.IsAvailable(serviceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	dbName, err := valueObject.NewDatabaseName(untrustedInput["dbName"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbUsername, err := valueObject.NewDatabaseUsername(untrustedInput["username"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbPassword, err := valueObject.NewPassword(untrustedInput["password"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbPrivileges := []valueObject.DatabasePrivilege{
+		valueObject.DatabasePrivilege("ALL"),
+	}
+	if untrustedInput["privileges"] != nil {
+		var assertOk bool
+		dbPrivileges, assertOk = untrustedInput["privileges"].([]valueObject.DatabasePrivilege)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidDatabasePrivileges")
+		}
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateDatabaseUser(
+		dbName, dbUsername, dbPassword, dbPrivileges, operatorAccountId,
+		operatorIpAddress,
+	)
+
+	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
+	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
+
+	err = useCase.CreateDatabaseUser(
+		databaseQueryRepo, databaseCmdRepo, liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "DatabaseUserCreated")
+}
+
+func (liaison *DatabaseLiaison) DeleteUser(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"dbType", "dbName", "dbUser"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbType, err := valueObject.NewDatabaseType(untrustedInput["dbType"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	serviceName, err := valueObject.NewServiceName(dbType.String())
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+	if !liaison.availabilityInspector.IsAvailable(serviceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	dbName, err := valueObject.NewDatabaseName(untrustedInput["dbName"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	dbUsername, err := valueObject.NewDatabaseUsername(untrustedInput["dbUser"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteDatabaseUser(
+		dbName, dbUsername, operatorAccountId, operatorIpAddress,
+	)
+
+	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
+	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
+
+	err = useCase.DeleteDatabaseUser(
+		databaseQueryRepo, databaseCmdRepo, liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "DatabaseUserDeleted")
+}

+ 12 - 12
src/presentation/service/helper/paginationParser.go → src/presentation/liaison/helper/paginationParser.go

@@ -1,4 +1,4 @@
-package serviceHelper
+package liaisonHelper
 
 import (
 	"errors"
@@ -9,45 +9,45 @@ import (
 )
 
 func PaginationParser(
-	userInput map[string]interface{},
+	untrustedInput map[string]any,
 	defaultPagination dto.Pagination,
 ) (requestPagination dto.Pagination, err error) {
 	requestPagination = defaultPagination
 
-	if userInput["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(userInput["pageNumber"])
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
 		if err != nil {
 			return requestPagination, errors.New("InvalidPageNumber")
 		}
 		requestPagination.PageNumber = pageNumber
 	}
 
-	if userInput["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(userInput["itemsPerPage"])
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
 		if err != nil {
 			return requestPagination, errors.New("InvalidItemsPerPage")
 		}
 		requestPagination.ItemsPerPage = itemsPerPage
 	}
 
-	if userInput["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(userInput["sortBy"])
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
 		if err != nil {
 			return requestPagination, err
 		}
 		requestPagination.SortBy = &sortBy
 	}
 
-	if userInput["sortDirection"] != nil {
-		sortDirection, err := valueObject.NewPaginationSortDirection(userInput["sortDirection"])
+	if untrustedInput["sortDirection"] != nil {
+		sortDirection, err := valueObject.NewPaginationSortDirection(untrustedInput["sortDirection"])
 		if err != nil {
 			return requestPagination, err
 		}
 		requestPagination.SortDirection = &sortDirection
 	}
 
-	if userInput["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(userInput["lastSeenId"])
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
 		if err != nil {
 			return requestPagination, err
 		}

+ 3 - 3
src/presentation/service/helper/requiredParamsInspector.go → src/presentation/liaison/helper/requiredParamsInspector.go

@@ -1,4 +1,4 @@
-package serviceHelper
+package liaisonHelper
 
 import (
 	"errors"
@@ -6,12 +6,12 @@ import (
 )
 
 func RequiredParamsInspector(
-	input map[string]interface{},
+	untrustedInput map[string]any,
 	requiredParams []string,
 ) error {
 	missingParams := []string{}
 	for _, param := range requiredParams {
-		if _, exists := input[param]; !exists {
+		if _, exists := untrustedInput[param]; !exists {
 			missingParams = append(missingParams, param)
 		}
 	}

+ 4 - 4
src/presentation/service/helper/timeParamsParser.go → src/presentation/liaison/helper/timeParamsParser.go

@@ -1,4 +1,4 @@
-package serviceHelper
+package liaisonHelper
 
 import (
 	"log/slog"
@@ -8,16 +8,16 @@ import (
 
 func TimeParamsParser(
 	timeParamNames []string,
-	userInput map[string]interface{},
+	untrustedInput map[string]any,
 ) map[string]*valueObject.UnixTime {
 	timeParamPtrs := map[string]*valueObject.UnixTime{}
 
 	for _, timeParamName := range timeParamNames {
-		if userInput[timeParamName] == nil {
+		if untrustedInput[timeParamName] == nil {
 			continue
 		}
 
-		timeParam, err := valueObject.NewUnixTime(userInput[timeParamName])
+		timeParam, err := valueObject.NewUnixTime(untrustedInput[timeParamName])
 		if err != nil {
 			slog.Debug("InvalidTimeParam", slog.String("timeParamName", timeParamName))
 			timeParamPtrs[timeParamName] = nil

+ 131 - 137
src/presentation/service/marketplace.go → src/presentation/liaison/marketplace.go

@@ -1,4 +1,4 @@
-package service
+package liaison
 
 import (
 	"errors"
@@ -17,21 +17,21 @@ import (
 	scheduledTaskInfra "github.com/goinfinite/os/src/infra/scheduledTask"
 	servicesInfra "github.com/goinfinite/os/src/infra/services"
 	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
 )
 
-type MarketplaceService struct {
+type MarketplaceLiaison struct {
 	marketplaceQueryRepo  *marketplaceInfra.MarketplaceQueryRepo
 	marketplaceCmdRepo    *marketplaceInfra.MarketplaceCmdRepo
 	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
 	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
 }
 
-func NewMarketplaceService(
+func NewMarketplaceLiaison(
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
 	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *MarketplaceService {
-	return &MarketplaceService{
+) *MarketplaceLiaison {
+	return &MarketplaceLiaison{
 		marketplaceQueryRepo:  marketplaceInfra.NewMarketplaceQueryRepo(persistentDbSvc),
 		marketplaceCmdRepo:    marketplaceInfra.NewMarketplaceCmdRepo(persistentDbSvc),
 		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
@@ -39,84 +39,84 @@ func NewMarketplaceService(
 	}
 }
 
-func (service *MarketplaceService) ReadCatalog(
-	input map[string]interface{},
-) ServiceOutput {
+func (liaison *MarketplaceLiaison) ReadCatalog(
+	untrustedInput map[string]any,
+) LiaisonOutput {
 	var idPtr *valueObject.MarketplaceItemId
-	if input["id"] != nil {
-		id, err := valueObject.NewMarketplaceItemId(input["id"])
+	if untrustedInput["id"] != nil {
+		id, err := valueObject.NewMarketplaceItemId(untrustedInput["id"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		idPtr = &id
 	}
 
 	var slugPtr *valueObject.MarketplaceItemSlug
-	if input["slug"] != nil {
-		slug, err := valueObject.NewMarketplaceItemSlug(input["slug"])
+	if untrustedInput["slug"] != nil {
+		slug, err := valueObject.NewMarketplaceItemSlug(untrustedInput["slug"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		slugPtr = &slug
 	}
 
 	var namePtr *valueObject.MarketplaceItemName
-	if input["name"] != nil {
-		name, err := valueObject.NewMarketplaceItemName(input["name"])
+	if untrustedInput["name"] != nil {
+		name, err := valueObject.NewMarketplaceItemName(untrustedInput["name"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		namePtr = &name
 	}
 
 	var typePtr *valueObject.MarketplaceItemType
-	if input["type"] != nil {
-		itemType, err := valueObject.NewMarketplaceItemType(input["type"])
+	if untrustedInput["type"] != nil {
+		itemType, err := valueObject.NewMarketplaceItemType(untrustedInput["type"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		typePtr = &itemType
 	}
 
 	paginationDto := useCase.MarketplaceDefaultPagination
-	if input["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(input["pageNumber"])
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
 		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidPageNumber"))
+			return NewLiaisonOutput(UserError, errors.New("InvalidPageNumber"))
 		}
 		paginationDto.PageNumber = pageNumber
 	}
 
-	if input["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(input["itemsPerPage"])
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
 		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidItemsPerPage"))
+			return NewLiaisonOutput(UserError, errors.New("InvalidItemsPerPage"))
 		}
 		paginationDto.ItemsPerPage = itemsPerPage
 	}
 
-	if input["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(input["sortBy"])
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.SortBy = &sortBy
 	}
 
-	if input["sortDirection"] != nil {
+	if untrustedInput["sortDirection"] != nil {
 		sortDirection, err := valueObject.NewPaginationSortDirection(
-			input["sortDirection"],
+			untrustedInput["sortDirection"],
 		)
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.SortDirection = &sortDirection
 	}
 
-	if input["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(input["lastSeenId"])
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.LastSeenId = &lastSeenId
 	}
@@ -129,66 +129,63 @@ func (service *MarketplaceService) ReadCatalog(
 		MarketplaceCatalogItemType: typePtr,
 	}
 
-	marketplaceQueryRepo := marketplaceInfra.NewMarketplaceQueryRepo(
-		service.persistentDbSvc,
-	)
-	itemsList, err := useCase.ReadMarketplaceCatalogItems(marketplaceQueryRepo, readDto)
+	itemsList, err := useCase.ReadMarketplaceCatalogItems(liaison.marketplaceQueryRepo, readDto)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Success, itemsList)
+	return NewLiaisonOutput(Success, itemsList)
 }
 
-func (service *MarketplaceService) InstallCatalogItem(
-	input map[string]interface{},
+func (liaison *MarketplaceLiaison) InstallCatalogItem(
+	untrustedInput map[string]any,
 	shouldSchedule bool,
-) ServiceOutput {
+) LiaisonOutput {
 	hostname, err := infraHelper.ReadPrimaryVirtualHostHostname()
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	if input["hostname"] != nil {
-		hostname, err = valueObject.NewFqdn(input["hostname"])
+	if untrustedInput["hostname"] != nil {
+		hostname, err = valueObject.NewFqdn(untrustedInput["hostname"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 	}
 
 	var idPtr *valueObject.MarketplaceItemId
-	if input["id"] != nil {
-		id, err := valueObject.NewMarketplaceItemId(input["id"])
+	if untrustedInput["id"] != nil {
+		id, err := valueObject.NewMarketplaceItemId(untrustedInput["id"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 		idPtr = &id
 	}
 
 	var slugPtr *valueObject.MarketplaceItemSlug
-	if input["slug"] != nil {
-		slug, err := valueObject.NewMarketplaceItemSlug(input["slug"])
+	if untrustedInput["slug"] != nil {
+		slug, err := valueObject.NewMarketplaceItemSlug(untrustedInput["slug"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 		slugPtr = &slug
 	}
 
 	var urlPathPtr *valueObject.UrlPath
-	if input["urlPath"] != nil {
-		urlPath, err := valueObject.NewUrlPath(input["urlPath"])
+	if untrustedInput["urlPath"] != nil {
+		urlPath, err := valueObject.NewUrlPath(untrustedInput["urlPath"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 		urlPathPtr = &urlPath
 	}
 
 	dataFields := []valueObject.MarketplaceInstallableItemDataField{}
-	if _, exists := input["dataFields"]; exists {
+	if _, exists := untrustedInput["dataFields"]; exists {
 		var assertOk bool
-		dataFields, assertOk = input["dataFields"].([]valueObject.MarketplaceInstallableItemDataField)
+		dataFields, assertOk = untrustedInput["dataFields"].([]valueObject.MarketplaceInstallableItemDataField)
 		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidDataFields")
+			return NewLiaisonOutput(UserError, "InvalidDataFields")
 		}
 	}
 
@@ -217,7 +214,7 @@ func (service *MarketplaceService) InstallCatalogItem(
 
 		cliCmd += " " + strings.Join(installParams, " ")
 
-		scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(service.persistentDbSvc)
+		scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(liaison.persistentDbSvc)
 		taskName, _ := valueObject.NewScheduledTaskName("InstallMarketplaceCatalogItem")
 		taskCmd, _ := valueObject.NewUnixCommand(cliCmd)
 		taskTag, _ := valueObject.NewScheduledTaskTag("marketplace")
@@ -230,25 +227,25 @@ func (service *MarketplaceService) InstallCatalogItem(
 
 		err = useCase.CreateScheduledTask(scheduledTaskCmdRepo, scheduledTaskCreateDto)
 		if err != nil {
-			return NewServiceOutput(InfraError, err.Error())
+			return NewLiaisonOutput(InfraError, err.Error())
 		}
 
-		return NewServiceOutput(Created, "MarketplaceCatalogItemInstallationScheduled")
+		return NewLiaisonOutput(Created, "MarketplaceCatalogItemInstallationScheduled")
 	}
 
 	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 	}
 
 	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 	}
 
@@ -257,99 +254,99 @@ func (service *MarketplaceService) InstallCatalogItem(
 		operatorIpAddress,
 	)
 
-	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(service.persistentDbSvc)
+	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(liaison.persistentDbSvc)
 
 	err = useCase.InstallMarketplaceCatalogItem(
-		vhostQueryRepo, service.marketplaceQueryRepo, service.marketplaceCmdRepo,
-		service.activityRecordCmdRepo, installDto,
+		vhostQueryRepo, liaison.marketplaceQueryRepo, liaison.marketplaceCmdRepo,
+		liaison.activityRecordCmdRepo, installDto,
 	)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Created, "MarketplaceCatalogItemInstalled")
+	return NewLiaisonOutput(Created, "MarketplaceCatalogItemInstalled")
 }
 
-func (service *MarketplaceService) ReadInstalledItems(
-	input map[string]interface{},
-) ServiceOutput {
+func (liaison *MarketplaceLiaison) ReadInstalledItems(
+	untrustedInput map[string]any,
+) LiaisonOutput {
 	var idPtr *valueObject.MarketplaceItemId
-	if input["id"] != nil {
-		id, err := valueObject.NewMarketplaceItemId(input["id"])
+	if untrustedInput["id"] != nil {
+		id, err := valueObject.NewMarketplaceItemId(untrustedInput["id"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		idPtr = &id
 	}
 
 	var hostnamePtr *valueObject.Fqdn
-	if input["hostname"] != nil {
-		hostname, err := valueObject.NewFqdn(input["hostname"])
+	if untrustedInput["hostname"] != nil {
+		hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		hostnamePtr = &hostname
 	}
 
 	var typePtr *valueObject.MarketplaceItemType
-	if input["type"] != nil {
-		itemType, err := valueObject.NewMarketplaceItemType(input["type"])
+	if untrustedInput["type"] != nil {
+		itemType, err := valueObject.NewMarketplaceItemType(untrustedInput["type"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		typePtr = &itemType
 	}
 
 	var installationUuidPtr *valueObject.MarketplaceInstalledItemUuid
-	if input["installationUuid"] != nil {
+	if untrustedInput["installationUuid"] != nil {
 		installationUuid, err := valueObject.NewMarketplaceInstalledItemUuid(
-			input["installationUuid"],
+			untrustedInput["installationUuid"],
 		)
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		installationUuidPtr = &installationUuid
 	}
 
 	paginationDto := useCase.MarketplaceDefaultPagination
-	if input["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(input["pageNumber"])
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
 		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidPageNumber"))
+			return NewLiaisonOutput(UserError, errors.New("InvalidPageNumber"))
 		}
 		paginationDto.PageNumber = pageNumber
 	}
 
-	if input["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(input["itemsPerPage"])
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
 		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidItemsPerPage"))
+			return NewLiaisonOutput(UserError, errors.New("InvalidItemsPerPage"))
 		}
 		paginationDto.ItemsPerPage = itemsPerPage
 	}
 
-	if input["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(input["sortBy"])
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.SortBy = &sortBy
 	}
 
-	if input["sortDirection"] != nil {
+	if untrustedInput["sortDirection"] != nil {
 		sortDirection, err := valueObject.NewPaginationSortDirection(
-			input["sortDirection"],
+			untrustedInput["sortDirection"],
 		)
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.SortDirection = &sortDirection
 	}
 
-	if input["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(input["lastSeenId"])
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.LastSeenId = &lastSeenId
 	}
@@ -362,39 +359,36 @@ func (service *MarketplaceService) ReadInstalledItems(
 		MarketplaceInstalledItemUuid:     installationUuidPtr,
 	}
 
-	marketplaceQueryRepo := marketplaceInfra.NewMarketplaceQueryRepo(
-		service.persistentDbSvc,
-	)
 	itemsList, err := useCase.ReadMarketplaceInstalledItems(
-		marketplaceQueryRepo, readDto,
+		liaison.marketplaceQueryRepo, readDto,
 	)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Success, itemsList)
+	return NewLiaisonOutput(Success, itemsList)
 }
 
-func (service *MarketplaceService) DeleteInstalledItem(
-	input map[string]interface{},
+func (liaison *MarketplaceLiaison) DeleteInstalledItem(
+	untrustedInput map[string]any,
 	shouldSchedule bool,
-) ServiceOutput {
+) LiaisonOutput {
 	requiredParams := []string{"installedId"}
 
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
-	installedId, err := valueObject.NewMarketplaceItemId(input["installedId"])
+	installedId, err := valueObject.NewMarketplaceItemId(untrustedInput["installedId"])
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
 	shouldUninstallServices := true
-	if input["shouldUninstallServices"] != nil {
+	if untrustedInput["shouldUninstallServices"] != nil {
 		shouldUninstallServices, err = voHelper.InterfaceToBool(
-			input["shouldUninstallServices"],
+			untrustedInput["shouldUninstallServices"],
 		)
 		if err != nil {
 			shouldUninstallServices = false
@@ -402,18 +396,18 @@ func (service *MarketplaceService) DeleteInstalledItem(
 	}
 
 	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 	}
 
 	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 	}
 
@@ -427,7 +421,7 @@ func (service *MarketplaceService) DeleteInstalledItem(
 		cliCmd += " " + strings.Join(installParams, " ")
 
 		scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(
-			service.persistentDbSvc,
+			liaison.persistentDbSvc,
 		)
 		taskName, _ := valueObject.NewScheduledTaskName("DeleteMarketplaceCatalogItem")
 		taskCmd, _ := valueObject.NewUnixCommand(cliCmd)
@@ -441,29 +435,29 @@ func (service *MarketplaceService) DeleteInstalledItem(
 
 		err = useCase.CreateScheduledTask(scheduledTaskCmdRepo, scheduledTaskCreateDto)
 		if err != nil {
-			return NewServiceOutput(InfraError, err.Error())
+			return NewLiaisonOutput(InfraError, err.Error())
 		}
 
-		return NewServiceOutput(Created, "MarketplaceCatalogItemDeletionScheduled")
+		return NewLiaisonOutput(Created, "MarketplaceCatalogItemDeletionScheduled")
 	}
 
 	deleteMarketplaceInstalledItem := dto.NewDeleteMarketplaceInstalledItem(
 		installedId, shouldUninstallServices, operatorAccountId, operatorIpAddress,
 	)
 
-	mappingQueryRepo := vhostInfra.NewMappingQueryRepo(service.persistentDbSvc)
-	mappingCmdRepo := vhostInfra.NewMappingCmdRepo(service.persistentDbSvc)
-	servicesQueryRepo := servicesInfra.NewServicesQueryRepo(service.persistentDbSvc)
-	servicesCmdRepo := servicesInfra.NewServicesCmdRepo(service.persistentDbSvc)
+	mappingQueryRepo := vhostInfra.NewMappingQueryRepo(liaison.persistentDbSvc)
+	mappingCmdRepo := vhostInfra.NewMappingCmdRepo(liaison.persistentDbSvc)
+	servicesQueryRepo := servicesInfra.NewServicesQueryRepo(liaison.persistentDbSvc)
+	servicesCmdRepo := servicesInfra.NewServicesCmdRepo(liaison.persistentDbSvc)
 
 	err = useCase.DeleteMarketplaceInstalledItem(
-		service.marketplaceQueryRepo, service.marketplaceCmdRepo,
+		liaison.marketplaceQueryRepo, liaison.marketplaceCmdRepo,
 		mappingQueryRepo, mappingCmdRepo, servicesQueryRepo, servicesCmdRepo,
-		service.activityRecordCmdRepo, deleteMarketplaceInstalledItem,
+		liaison.activityRecordCmdRepo, deleteMarketplaceInstalledItem,
 	)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Success, "MarketplaceInstalledItemDeleted")
+	return NewLiaisonOutput(Success, "MarketplaceInstalledItemDeleted")
 }

+ 9 - 9
src/presentation/service/o11y.go → src/presentation/liaison/o11y.go

@@ -1,4 +1,4 @@
-package service
+package liaison
 
 import (
 	"github.com/goinfinite/os/src/domain/useCase"
@@ -6,25 +6,25 @@ import (
 	o11yInfra "github.com/goinfinite/os/src/infra/o11y"
 )
 
-type O11yService struct {
+type O11yLiaison struct {
 	transientDbSvc *internalDbInfra.TransientDatabaseService
 }
 
-func NewO11yService(
+func NewO11yLiaison(
 	transientDbSvc *internalDbInfra.TransientDatabaseService,
-) *O11yService {
-	return &O11yService{
+) *O11yLiaison {
+	return &O11yLiaison{
 		transientDbSvc: transientDbSvc,
 	}
 }
 
-func (service *O11yService) ReadOverview() ServiceOutput {
-	o11yQueryRepo := o11yInfra.NewO11yQueryRepo(service.transientDbSvc)
+func (liaison *O11yLiaison) ReadOverview() LiaisonOutput {
+	o11yQueryRepo := o11yInfra.NewO11yQueryRepo(liaison.transientDbSvc)
 
 	o11yOverview, err := useCase.ReadO11yOverview(o11yQueryRepo, true)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Success, o11yOverview)
+	return NewLiaisonOutput(Success, o11yOverview)
 }

+ 6 - 6
src/presentation/service/output.go → src/presentation/liaison/output.go

@@ -1,4 +1,4 @@
-package service
+package liaison
 
 type StatusEnum string
 
@@ -11,13 +11,13 @@ const (
 	InfraError   StatusEnum = "infraError"
 )
 
-type ServiceOutput struct {
-	Status StatusEnum  `json:"status"`
-	Body   interface{} `json:"body"`
+type LiaisonOutput struct {
+	Status StatusEnum `json:"status"`
+	Body   any        `json:"body"`
 }
 
-func NewServiceOutput(status StatusEnum, body interface{}) ServiceOutput {
-	return ServiceOutput{
+func NewLiaisonOutput(status StatusEnum, body any) LiaisonOutput {
+	return LiaisonOutput{
 		Status: status,
 		Body:   body,
 	}

+ 200 - 0
src/presentation/liaison/runtime.go

@@ -0,0 +1,200 @@
+package liaison
+
+import (
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/entity"
+	"github.com/goinfinite/os/src/domain/useCase"
+	"github.com/goinfinite/os/src/domain/valueObject"
+	accountInfra "github.com/goinfinite/os/src/infra/account"
+	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
+	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
+	runtimeInfra "github.com/goinfinite/os/src/infra/runtime"
+	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
+	sharedHelper "github.com/goinfinite/os/src/presentation/shared/helper"
+	tkVoUtil "github.com/goinfinite/tk/src/domain/valueObject/util"
+)
+
+type RuntimeLiaison struct {
+	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
+	availabilityInspector *sharedHelper.ServiceAvailabilityInspector
+	runtimeQueryRepo      runtimeInfra.RuntimeQueryRepo
+	runtimeCmdRepo        *runtimeInfra.RuntimeCmdRepo
+	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
+	phpServiceName        valueObject.ServiceName
+}
+
+func NewRuntimeLiaison(
+	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
+	trailDbSvc *internalDbInfra.TrailDatabaseService,
+) *RuntimeLiaison {
+	return &RuntimeLiaison{
+		persistentDbSvc: persistentDbSvc,
+		availabilityInspector: sharedHelper.NewServiceAvailabilityInspector(
+			persistentDbSvc,
+		),
+		runtimeQueryRepo:      runtimeInfra.RuntimeQueryRepo{},
+		runtimeCmdRepo:        runtimeInfra.NewRuntimeCmdRepo(persistentDbSvc),
+		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
+		phpServiceName:        valueObject.ServiceName("php-webserver"),
+	}
+}
+
+func (liaison *RuntimeLiaison) ReadPhpConfigs(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	if !liaison.availabilityInspector.IsAvailable(liaison.phpServiceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	phpConfigs, err := useCase.ReadPhpConfigs(liaison.runtimeQueryRepo, hostname)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, phpConfigs)
+}
+
+func (liaison *RuntimeLiaison) UpdatePhpConfigs(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	if !liaison.availabilityInspector.IsAvailable(liaison.phpServiceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	requiredParams := []string{"hostname", "version"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	phpVersion, err := valueObject.NewPhpVersion(untrustedInput["version"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	phpModules := []entity.PhpModule{}
+	if _, exists := untrustedInput["modules"]; exists {
+		var assertOk bool
+		phpModules, assertOk = untrustedInput["modules"].([]entity.PhpModule)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidPhpModules")
+		}
+	}
+
+	phpSettings := []entity.PhpSetting{}
+	if _, exists := untrustedInput["settings"]; exists {
+		var assertOk bool
+		phpSettings, assertOk = untrustedInput["settings"].([]entity.PhpSetting)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidPhpSettings")
+		}
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	updateDto := dto.NewUpdatePhpConfigs(
+		hostname, phpVersion, phpModules, phpSettings, operatorAccountId,
+		operatorIpAddress,
+	)
+
+	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(liaison.persistentDbSvc)
+
+	err = useCase.UpdatePhpConfigs(
+		liaison.runtimeQueryRepo, liaison.runtimeCmdRepo, vhostQueryRepo,
+		liaison.activityRecordCmdRepo, updateDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "PhpConfigsUpdated")
+}
+
+func (liaison *RuntimeLiaison) RunPhpCommand(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	if !liaison.availabilityInspector.IsAvailable(liaison.phpServiceName) {
+		return NewLiaisonOutput(InfraError, sharedHelper.ServiceUnavailableError)
+	}
+
+	requiredParams := []string{"hostname", "command"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	command, err := valueObject.NewUnixCommand(untrustedInput["command"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var timeoutSecsPtr *uint64
+	if untrustedInput["timeoutSecs"] != nil {
+		timeoutSecs, err := tkVoUtil.InterfaceToUint64(untrustedInput["timeoutSecs"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "TimeoutSecsMustBeUint64")
+		}
+		timeoutSecsPtr = &timeoutSecs
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	runRequest := dto.NewRunPhpCommandRequest(
+		hostname, command, timeoutSecsPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	accountQueryRepo := accountInfra.NewAccountQueryRepo(liaison.persistentDbSvc)
+
+	runResponse, err := useCase.RunPhpCommand(
+		accountQueryRepo, liaison.runtimeCmdRepo, runRequest,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, runResponse)
+}

+ 59 - 59
src/presentation/service/scheduledTask.go → src/presentation/liaison/scheduledTask.go

@@ -1,4 +1,4 @@
-package service
+package liaison
 
 import (
 	"errors"
@@ -9,60 +9,60 @@ import (
 	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
 	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
 	scheduledTaskInfra "github.com/goinfinite/os/src/infra/scheduledTask"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
 	"golang.org/x/text/cases"
 	"golang.org/x/text/language"
 )
 
-type ScheduledTaskService struct {
+type ScheduledTaskLiaison struct {
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService
 }
 
-func NewScheduledTaskService(
+func NewScheduledTaskLiaison(
 	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
-) *ScheduledTaskService {
-	return &ScheduledTaskService{
+) *ScheduledTaskLiaison {
+	return &ScheduledTaskLiaison{
 		persistentDbSvc: persistentDbSvc,
 	}
 }
 
-func (service *ScheduledTaskService) Read(input map[string]interface{}) ServiceOutput {
+func (liaison *ScheduledTaskLiaison) Read(untrustedInput map[string]any) LiaisonOutput {
 	var taskIdPtr *valueObject.ScheduledTaskId
-	if input["id"] != nil {
-		input["taskId"] = input["id"]
+	if untrustedInput["id"] != nil {
+		untrustedInput["taskId"] = untrustedInput["id"]
 	}
-	if input["taskId"] != nil {
-		taskId, err := valueObject.NewScheduledTaskId(input["taskId"])
+	if untrustedInput["taskId"] != nil {
+		taskId, err := valueObject.NewScheduledTaskId(untrustedInput["taskId"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		taskIdPtr = &taskId
 	}
 
 	var taskNamePtr *valueObject.ScheduledTaskName
-	if input["taskName"] != nil {
-		taskName, err := valueObject.NewScheduledTaskName(input["taskName"])
+	if untrustedInput["taskName"] != nil {
+		taskName, err := valueObject.NewScheduledTaskName(untrustedInput["taskName"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		taskNamePtr = &taskName
 	}
 
 	var taskStatusPtr *valueObject.ScheduledTaskStatus
-	if input["taskStatus"] != nil {
-		taskStatus, err := valueObject.NewScheduledTaskStatus(input["taskStatus"])
+	if untrustedInput["taskStatus"] != nil {
+		taskStatus, err := valueObject.NewScheduledTaskStatus(untrustedInput["taskStatus"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		taskStatusPtr = &taskStatus
 	}
 
 	taskTags := []valueObject.ScheduledTaskTag{}
-	if input["taskTags"] != nil {
+	if untrustedInput["taskTags"] != nil {
 		var assertOk bool
-		taskTags, assertOk = input["taskTags"].([]valueObject.ScheduledTaskTag)
+		taskTags, assertOk = untrustedInput["taskTags"].([]valueObject.ScheduledTaskTag)
 		if !assertOk {
-			return NewServiceOutput(UserError, errors.New("InvalidTaskTags"))
+			return NewLiaisonOutput(UserError, errors.New("InvalidTaskTags"))
 		}
 	}
 
@@ -76,14 +76,14 @@ func (service *ScheduledTaskService) Read(input map[string]interface{}) ServiceO
 		"createdBeforeAt", "createdAfterAt",
 	}
 	for _, timeParamName := range timeParamNames {
-		if input[timeParamName] == nil {
+		if untrustedInput[timeParamName] == nil {
 			continue
 		}
 
-		timeParam, err := valueObject.NewUnixTime(input[timeParamName])
+		timeParam, err := valueObject.NewUnixTime(untrustedInput[timeParamName])
 		if err != nil {
 			capitalParamName := cases.Title(language.English).String(timeParamName)
-			return NewServiceOutput(UserError, errors.New("Invalid"+capitalParamName))
+			return NewLiaisonOutput(UserError, errors.New("Invalid"+capitalParamName))
 		}
 
 		switch timeParamName {
@@ -103,42 +103,42 @@ func (service *ScheduledTaskService) Read(input map[string]interface{}) ServiceO
 	}
 
 	paginationDto := useCase.ScheduledTasksDefaultPagination
-	if input["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(input["pageNumber"])
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
 		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidPageNumber"))
+			return NewLiaisonOutput(UserError, errors.New("InvalidPageNumber"))
 		}
 		paginationDto.PageNumber = pageNumber
 	}
 
-	if input["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(input["itemsPerPage"])
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
 		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidItemsPerPage"))
+			return NewLiaisonOutput(UserError, errors.New("InvalidItemsPerPage"))
 		}
 		paginationDto.ItemsPerPage = itemsPerPage
 	}
 
-	if input["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(input["sortBy"])
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.SortBy = &sortBy
 	}
 
-	if input["sortDirection"] != nil {
-		sortDirection, err := valueObject.NewPaginationSortDirection(input["sortDirection"])
+	if untrustedInput["sortDirection"] != nil {
+		sortDirection, err := valueObject.NewPaginationSortDirection(untrustedInput["sortDirection"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.SortDirection = &sortDirection
 	}
 
-	if input["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(input["lastSeenId"])
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
 		if err != nil {
-			return NewServiceOutput(UserError, err)
+			return NewLiaisonOutput(UserError, err)
 		}
 		paginationDto.LastSeenId = &lastSeenId
 	}
@@ -157,46 +157,46 @@ func (service *ScheduledTaskService) Read(input map[string]interface{}) ServiceO
 		CreatedAfterAt:   createdAfterAtPtr,
 	}
 
-	scheduledTaskQueryRepo := scheduledTaskInfra.NewScheduledTaskQueryRepo(service.persistentDbSvc)
+	scheduledTaskQueryRepo := scheduledTaskInfra.NewScheduledTaskQueryRepo(liaison.persistentDbSvc)
 	scheduledTasksList, err := useCase.ReadScheduledTasks(scheduledTaskQueryRepo, readDto)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Success, scheduledTasksList)
+	return NewLiaisonOutput(Success, scheduledTasksList)
 }
 
-func (service *ScheduledTaskService) Update(input map[string]interface{}) ServiceOutput {
-	if input["id"] != nil {
-		input["taskId"] = input["id"]
+func (liaison *ScheduledTaskLiaison) Update(untrustedInput map[string]any) LiaisonOutput {
+	if untrustedInput["id"] != nil {
+		untrustedInput["taskId"] = untrustedInput["id"]
 	}
 
 	requiredParams := []string{"taskId"}
 
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
-	taskId, err := valueObject.NewScheduledTaskId(input["taskId"])
+	taskId, err := valueObject.NewScheduledTaskId(untrustedInput["taskId"])
 	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
+		return NewLiaisonOutput(UserError, err.Error())
 	}
 
 	var taskStatusPtr *valueObject.ScheduledTaskStatus
-	if input["status"] != nil {
-		taskStatus, err := valueObject.NewScheduledTaskStatus(input["status"])
+	if untrustedInput["status"] != nil {
+		taskStatus, err := valueObject.NewScheduledTaskStatus(untrustedInput["status"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 		taskStatusPtr = &taskStatus
 	}
 
 	var runAtPtr *valueObject.UnixTime
-	if input["runAt"] != nil {
-		runAt, err := valueObject.NewUnixTime(input["runAt"])
+	if untrustedInput["runAt"] != nil {
+		runAt, err := valueObject.NewUnixTime(untrustedInput["runAt"])
 		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
+			return NewLiaisonOutput(UserError, err.Error())
 		}
 		runAtPtr = &runAt
 	}
@@ -205,15 +205,15 @@ func (service *ScheduledTaskService) Update(input map[string]interface{}) Servic
 		taskId, taskStatusPtr, runAtPtr,
 	)
 
-	scheduledTaskQueryRepo := scheduledTaskInfra.NewScheduledTaskQueryRepo(service.persistentDbSvc)
-	scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(service.persistentDbSvc)
+	scheduledTaskQueryRepo := scheduledTaskInfra.NewScheduledTaskQueryRepo(liaison.persistentDbSvc)
+	scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(liaison.persistentDbSvc)
 
 	err = useCase.UpdateScheduledTask(
 		scheduledTaskQueryRepo, scheduledTaskCmdRepo, updateDto,
 	)
 	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
+		return NewLiaisonOutput(InfraError, err.Error())
 	}
 
-	return NewServiceOutput(Success, "ScheduledTaskUpdated")
+	return NewLiaisonOutput(Success, "ScheduledTaskUpdated")
 }

+ 937 - 0
src/presentation/liaison/services.go

@@ -0,0 +1,937 @@
+package liaison
+
+import (
+	"errors"
+	"log/slog"
+	"strconv"
+	"strings"
+
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/useCase"
+	"github.com/goinfinite/os/src/domain/valueObject"
+	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
+	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
+	infraEnvs "github.com/goinfinite/os/src/infra/envs"
+	infraHelper "github.com/goinfinite/os/src/infra/helper"
+	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
+	scheduledTaskInfra "github.com/goinfinite/os/src/infra/scheduledTask"
+	servicesInfra "github.com/goinfinite/os/src/infra/services"
+	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
+)
+
+type ServicesLiaison struct {
+	persistentDbService   *internalDbInfra.PersistentDatabaseService
+	servicesQueryRepo     *servicesInfra.ServicesQueryRepo
+	servicesCmdRepo       *servicesInfra.ServicesCmdRepo
+	mappingQueryRepo      *vhostInfra.MappingQueryRepo
+	mappingCmdRepo        *vhostInfra.MappingCmdRepo
+	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
+}
+
+func NewServicesLiaison(
+	persistentDbService *internalDbInfra.PersistentDatabaseService,
+	trailDbSvc *internalDbInfra.TrailDatabaseService,
+) *ServicesLiaison {
+	return &ServicesLiaison{
+		persistentDbService:   persistentDbService,
+		servicesQueryRepo:     servicesInfra.NewServicesQueryRepo(persistentDbService),
+		servicesCmdRepo:       servicesInfra.NewServicesCmdRepo(persistentDbService),
+		mappingQueryRepo:      vhostInfra.NewMappingQueryRepo(persistentDbService),
+		mappingCmdRepo:        vhostInfra.NewMappingCmdRepo(persistentDbService),
+		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
+	}
+}
+
+func (liaison *ServicesLiaison) ReadInstalledItems(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	var namePtr *valueObject.ServiceName
+	if untrustedInput["name"] != nil {
+		name, err := valueObject.NewServiceName(untrustedInput["name"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		namePtr = &name
+	}
+
+	var naturePtr *valueObject.ServiceNature
+	if untrustedInput["nature"] != nil {
+		nature, err := valueObject.NewServiceNature(untrustedInput["nature"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		naturePtr = &nature
+	}
+
+	var statusPtr *valueObject.ServiceStatus
+	if untrustedInput["status"] != nil {
+		status, err := valueObject.NewServiceStatus(untrustedInput["status"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		statusPtr = &status
+	}
+
+	var typePtr *valueObject.ServiceType
+	if untrustedInput["type"] != nil {
+		itemType, err := valueObject.NewServiceType(untrustedInput["type"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		typePtr = &itemType
+	}
+
+	shouldIncludeMetrics := false
+	if untrustedInput["shouldIncludeMetrics"] != nil {
+		var err error
+		shouldIncludeMetrics, err = voHelper.InterfaceToBool(untrustedInput["shouldIncludeMetrics"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+	}
+
+	paginationDto := useCase.ServicesDefaultPagination
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidPageNumber"))
+		}
+		paginationDto.PageNumber = pageNumber
+	}
+
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidItemsPerPage"))
+		}
+		paginationDto.ItemsPerPage = itemsPerPage
+	}
+
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortBy = &sortBy
+	}
+
+	if untrustedInput["sortDirection"] != nil {
+		sortDirection, err := valueObject.NewPaginationSortDirection(
+			untrustedInput["sortDirection"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortDirection = &sortDirection
+	}
+
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.LastSeenId = &lastSeenId
+	}
+
+	readDto := dto.ReadInstalledServicesItemsRequest{
+		Pagination:           paginationDto,
+		ServiceName:          namePtr,
+		ServiceNature:        naturePtr,
+		ServiceType:          typePtr,
+		ServiceStatus:        statusPtr,
+		ShouldIncludeMetrics: &shouldIncludeMetrics,
+	}
+
+	servicesList, err := useCase.ReadInstalledServices(
+		liaison.servicesQueryRepo, readDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, servicesList)
+}
+
+func (liaison *ServicesLiaison) ReadInstallableItems(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	var namePtr *valueObject.ServiceName
+	if untrustedInput["name"] != nil {
+		name, err := valueObject.NewServiceName(untrustedInput["name"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		namePtr = &name
+	}
+
+	var naturePtr *valueObject.ServiceNature
+	if untrustedInput["nature"] != nil {
+		nature, err := valueObject.NewServiceNature(untrustedInput["nature"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		naturePtr = &nature
+	}
+
+	var typePtr *valueObject.ServiceType
+	if untrustedInput["type"] != nil {
+		itemType, err := valueObject.NewServiceType(untrustedInput["type"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		typePtr = &itemType
+	}
+
+	paginationDto := useCase.ServicesDefaultPagination
+	if untrustedInput["pageNumber"] != nil {
+		pageNumber, err := voHelper.InterfaceToUint32(untrustedInput["pageNumber"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidPageNumber"))
+		}
+		paginationDto.PageNumber = pageNumber
+	}
+
+	if untrustedInput["itemsPerPage"] != nil {
+		itemsPerPage, err := voHelper.InterfaceToUint16(untrustedInput["itemsPerPage"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidItemsPerPage"))
+		}
+		paginationDto.ItemsPerPage = itemsPerPage
+	}
+
+	if untrustedInput["sortBy"] != nil {
+		sortBy, err := valueObject.NewPaginationSortBy(untrustedInput["sortBy"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortBy = &sortBy
+	}
+
+	if untrustedInput["sortDirection"] != nil {
+		sortDirection, err := valueObject.NewPaginationSortDirection(
+			untrustedInput["sortDirection"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.SortDirection = &sortDirection
+	}
+
+	if untrustedInput["lastSeenId"] != nil {
+		lastSeenId, err := valueObject.NewPaginationLastSeenId(untrustedInput["lastSeenId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err)
+		}
+		paginationDto.LastSeenId = &lastSeenId
+	}
+
+	readDto := dto.ReadInstallableServicesItemsRequest{
+		Pagination:    paginationDto,
+		ServiceName:   namePtr,
+		ServiceNature: naturePtr,
+		ServiceType:   typePtr,
+	}
+
+	servicesList, err := useCase.ReadInstallableServices(
+		liaison.servicesQueryRepo, readDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, servicesList)
+}
+
+func (liaison *ServicesLiaison) CreateInstallable(
+	untrustedInput map[string]any,
+	shouldSchedule bool,
+) LiaisonOutput {
+	requiredParams := []string{"name"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	name, err := valueObject.NewServiceName(untrustedInput["name"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var versionPtr *valueObject.ServiceVersion
+	if untrustedInput["version"] != nil {
+		version, err := valueObject.NewServiceVersion(untrustedInput["version"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		versionPtr = &version
+	}
+
+	var startupFilePtr *valueObject.UnixFilePath
+	if untrustedInput["startupFile"] != nil {
+		startupFile, err := valueObject.NewUnixFilePath(untrustedInput["startupFile"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		startupFilePtr = &startupFile
+	}
+
+	var workingDirPtr *valueObject.UnixFilePath
+	if untrustedInput["workingDir"] != nil {
+		workingDir, err := valueObject.NewUnixFilePath(untrustedInput["workingDir"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		workingDirPtr = &workingDir
+	}
+
+	envs := []valueObject.ServiceEnv{}
+	if untrustedInput["envs"] != nil {
+		var assertOk bool
+		envs, assertOk = untrustedInput["envs"].([]valueObject.ServiceEnv)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidServiceEnvs")
+		}
+	}
+
+	portBindings := []valueObject.PortBinding{}
+	if untrustedInput["portBindings"] != nil {
+		var assertOk bool
+		portBindings, assertOk = untrustedInput["portBindings"].([]valueObject.PortBinding)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidPortBindings")
+		}
+	}
+
+	var autoStartPtr *bool
+	if untrustedInput["autoStart"] != nil {
+		autoStart, err := voHelper.InterfaceToBool(untrustedInput["autoStart"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "AutoStartMustBeBool")
+		}
+		autoStartPtr = &autoStart
+	}
+
+	var timeoutStartSecsPtr *uint
+	if untrustedInput["timeoutStartSecs"] != nil {
+		timeoutStartSecs, err := voHelper.InterfaceToUint(untrustedInput["timeoutStartSecs"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "TimeoutStartSecsMustBeUint")
+		}
+		timeoutStartSecsPtr = &timeoutStartSecs
+	}
+
+	var autoRestartPtr *bool
+	if untrustedInput["autoRestart"] != nil {
+		autoRestart, err := voHelper.InterfaceToBool(
+			untrustedInput["autoRestart"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, "AutoRestartMustBeBool")
+		}
+		autoRestartPtr = &autoRestart
+	}
+
+	var maxStartRetriesPtr *uint
+	if untrustedInput["maxStartRetries"] != nil {
+		maxStartRetries, err := voHelper.InterfaceToUint(untrustedInput["maxStartRetries"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "MaxStartRetriesMustBeUint")
+		}
+		maxStartRetriesPtr = &maxStartRetries
+	}
+
+	autoCreateMapping := true
+	if untrustedInput["autoCreateMapping"] != nil {
+		autoCreateMapping, err = voHelper.InterfaceToBool(untrustedInput["autoCreateMapping"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "AutoCreateMappingMustBeBool")
+		}
+	}
+
+	var mappingHostnamePtr *valueObject.Fqdn
+	if untrustedInput["mappingHostname"] != nil {
+		mappingHostname, err := valueObject.NewFqdn(untrustedInput["mappingHostname"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		mappingHostnamePtr = &mappingHostname
+	}
+
+	var mappingPathPtr *valueObject.MappingPath
+	if untrustedInput["mappingPath"] != nil {
+		mappingPath, err := valueObject.NewMappingPath(untrustedInput["mappingPath"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		mappingPathPtr = &mappingPath
+	}
+
+	var mappingUpgradeInsecureRequestsPtr *bool
+	if untrustedInput["mappingUpgradeInsecureRequests"] != nil {
+		mappingUpgradeInsecureRequests, err := voHelper.InterfaceToBool(
+			untrustedInput["mappingUpgradeInsecureRequests"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidMappingUpgradeInsecureRequests")
+		}
+		mappingUpgradeInsecureRequestsPtr = &mappingUpgradeInsecureRequests
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	if shouldSchedule {
+		cliCmd := infraEnvs.InfiniteOsBinary + " services create-installable"
+		installParams := []string{
+			"--name", name.String(),
+			"--auto-create-mapping", strconv.FormatBool(autoCreateMapping),
+		}
+
+		if len(envs) > 0 {
+			for _, env := range envs {
+				escapedField := infraHelper.ShellEscape{}.Quote(env.String())
+				installParams = append(installParams, "--envs", escapedField)
+			}
+		}
+
+		if len(portBindings) > 0 {
+			for _, portBinding := range portBindings {
+				escapedField := infraHelper.ShellEscape{}.Quote(portBinding.String())
+				installParams = append(installParams, "--port-bindings", escapedField)
+			}
+		}
+
+		if versionPtr != nil {
+			installParams = append(installParams, "--version", versionPtr.String())
+		}
+
+		if startupFilePtr != nil {
+			installParams = append(installParams, "--startup-file", startupFilePtr.String())
+		}
+
+		if workingDirPtr != nil {
+			installParams = append(installParams, "--working-dir", workingDirPtr.String())
+		}
+
+		if autoStartPtr != nil {
+			autoStartStr := strconv.FormatBool(*autoStartPtr)
+			installParams = append(installParams, "--auto-start", autoStartStr)
+		}
+
+		if timeoutStartSecsPtr != nil {
+			timeoutStartSecsStr := strconv.FormatUint(uint64(*timeoutStartSecsPtr), 10)
+			installParams = append(installParams, "--timeout-start-secs", timeoutStartSecsStr)
+		}
+
+		if autoRestartPtr != nil {
+			autoRestartStr := strconv.FormatBool(*autoRestartPtr)
+			installParams = append(installParams, "--auto-restart", autoRestartStr)
+		}
+
+		if maxStartRetriesPtr != nil {
+			maxStartRetriesStr := strconv.FormatUint(uint64(*maxStartRetriesPtr), 10)
+			installParams = append(installParams, "--max-start-retries", maxStartRetriesStr)
+		}
+
+		if mappingHostnamePtr != nil {
+			installParams = append(installParams, "--mapping-hostname", mappingHostnamePtr.String())
+		}
+
+		if mappingPathPtr != nil {
+			installParams = append(installParams, "--mapping-path", mappingPathPtr.String())
+		}
+
+		if mappingUpgradeInsecureRequestsPtr != nil {
+			installParams = append(
+				installParams,
+				"--mapping-upgrade-insecure-requests",
+				strconv.FormatBool(*mappingUpgradeInsecureRequestsPtr),
+			)
+		}
+
+		cliCmd += " " + strings.Join(installParams, " ")
+
+		scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(liaison.persistentDbService)
+		taskName, _ := valueObject.NewScheduledTaskName("CreateInstallableService")
+		taskCmd, _ := valueObject.NewUnixCommand(cliCmd)
+		taskTag, _ := valueObject.NewScheduledTaskTag("services")
+		taskTags := []valueObject.ScheduledTaskTag{taskTag}
+		timeoutSecs := uint16(1800)
+
+		scheduledTaskCreateDto := dto.NewCreateScheduledTask(
+			taskName, taskCmd, taskTags, &timeoutSecs, nil,
+		)
+
+		err = useCase.CreateScheduledTask(scheduledTaskCmdRepo, scheduledTaskCreateDto)
+		if err != nil {
+			return NewLiaisonOutput(InfraError, err.Error())
+		}
+
+		return NewLiaisonOutput(Created, "CreateInstallableServiceScheduled")
+	}
+
+	createDto := dto.NewCreateInstallableService(
+		name, envs, portBindings, versionPtr, startupFilePtr, workingDirPtr,
+		autoStartPtr, timeoutStartSecsPtr, autoRestartPtr, maxStartRetriesPtr,
+		&autoCreateMapping, mappingHostnamePtr, mappingPathPtr,
+		mappingUpgradeInsecureRequestsPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(liaison.persistentDbService)
+
+	err = useCase.CreateInstallableService(
+		liaison.servicesQueryRepo, liaison.servicesCmdRepo, vhostQueryRepo,
+		liaison.mappingCmdRepo, liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "InstallableServiceCreated")
+}
+
+func (liaison *ServicesLiaison) CreateCustom(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"name", "type", "startCmd"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	name, err := valueObject.NewServiceName(untrustedInput["name"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	svcType, err := valueObject.NewServiceType(untrustedInput["type"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	startCmd, err := valueObject.NewUnixCommand(untrustedInput["startCmd"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var versionPtr *valueObject.ServiceVersion
+	if untrustedInput["version"] != nil {
+		version, err := valueObject.NewServiceVersion(untrustedInput["version"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		versionPtr = &version
+	}
+
+	var execUserPtr *valueObject.UnixUsername
+	if untrustedInput["execUser"] != nil {
+		execUser, err := valueObject.NewUnixUsername(untrustedInput["execUser"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		execUserPtr = &execUser
+	}
+
+	envs := []valueObject.ServiceEnv{}
+	if untrustedInput["envs"] != nil {
+		var assertOk bool
+		envs, assertOk = untrustedInput["envs"].([]valueObject.ServiceEnv)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidServiceEnvs")
+		}
+	}
+
+	portBindings := []valueObject.PortBinding{}
+	if untrustedInput["portBindings"] != nil {
+		var assertOk bool
+		portBindings, assertOk = untrustedInput["portBindings"].([]valueObject.PortBinding)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidPortBindings")
+		}
+	}
+
+	var autoStartPtr *bool
+	if untrustedInput["autoStart"] != nil {
+		autoStart, err := voHelper.InterfaceToBool(untrustedInput["autoStart"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "AutoStartMustBeBool")
+		}
+		autoStartPtr = &autoStart
+	}
+
+	var timeoutStartSecsPtr *uint
+	if untrustedInput["timeoutStartSecs"] != nil {
+		timeoutStartSecs, err := voHelper.InterfaceToUint(untrustedInput["timeoutStartSecs"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "TimeoutStartSecsMustBeUint")
+		}
+		timeoutStartSecsPtr = &timeoutStartSecs
+	}
+
+	var autoRestartPtr *bool
+	if untrustedInput["autoRestart"] != nil {
+		autoRestart, err := voHelper.InterfaceToBool(
+			untrustedInput["autoRestart"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, "AutoRestartMustBeBool")
+		}
+		autoRestartPtr = &autoRestart
+	}
+
+	var maxStartRetriesPtr *uint
+	if untrustedInput["maxStartRetries"] != nil {
+		maxStartRetries, err := voHelper.InterfaceToUint(untrustedInput["maxStartRetries"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "MaxStartRetriesMustBeUint")
+		}
+		maxStartRetriesPtr = &maxStartRetries
+	}
+
+	var logOutputPathPtr *valueObject.UnixFilePath
+	if untrustedInput["logOutputPath"] != nil {
+		logOutputPath, err := valueObject.NewUnixFilePath(untrustedInput["logOutputPath"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		logOutputPathPtr = &logOutputPath
+	}
+
+	var logErrorPathPtr *valueObject.UnixFilePath
+	if untrustedInput["logErrorPath"] != nil {
+		logErrorPath, err := valueObject.NewUnixFilePath(untrustedInput["logErrorPath"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		logErrorPathPtr = &logErrorPath
+	}
+
+	var avatarUrlPtr *valueObject.Url
+	if untrustedInput["avatarUrl"] != nil {
+		avatarUrl, err := valueObject.NewUrl(untrustedInput["avatarUrl"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		avatarUrlPtr = &avatarUrl
+	}
+
+	autoCreateMapping := true
+	if untrustedInput["autoCreateMapping"] != nil {
+		autoCreateMapping, err = voHelper.InterfaceToBool(untrustedInput["autoCreateMapping"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "AutoCreateMappingMustBeBool")
+		}
+	}
+
+	var mappingHostnamePtr *valueObject.Fqdn
+	if untrustedInput["mappingHostname"] != nil {
+		mappingHostname, err := valueObject.NewFqdn(untrustedInput["mappingHostname"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		mappingHostnamePtr = &mappingHostname
+	}
+
+	var mappingPathPtr *valueObject.MappingPath
+	if untrustedInput["mappingPath"] != nil {
+		mappingPath, err := valueObject.NewMappingPath(untrustedInput["mappingPath"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		mappingPathPtr = &mappingPath
+	}
+
+	var mappingUpgradeInsecureRequestsPtr *bool
+	if untrustedInput["mappingUpgradeInsecureRequests"] != nil {
+		mappingUpgradeInsecureRequests, err := voHelper.InterfaceToBool(
+			untrustedInput["mappingUpgradeInsecureRequests"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidMappingUpgradeInsecureRequests")
+		}
+		mappingUpgradeInsecureRequestsPtr = &mappingUpgradeInsecureRequests
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createCustomDto := dto.NewCreateCustomService(
+		name, svcType, startCmd, envs, portBindings, nil, nil, nil, nil, nil,
+		versionPtr, execUserPtr, nil, autoStartPtr, autoRestartPtr,
+		timeoutStartSecsPtr, maxStartRetriesPtr, logOutputPathPtr, logErrorPathPtr,
+		avatarUrlPtr, &autoCreateMapping, mappingHostnamePtr, mappingPathPtr,
+		mappingUpgradeInsecureRequestsPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(liaison.persistentDbService)
+
+	err = useCase.CreateCustomService(
+		liaison.servicesQueryRepo, liaison.servicesCmdRepo, vhostQueryRepo,
+		liaison.mappingCmdRepo, liaison.activityRecordCmdRepo, createCustomDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "CustomServiceCreated")
+}
+
+func (liaison *ServicesLiaison) Update(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"name"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	name, err := valueObject.NewServiceName(untrustedInput["name"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var typePtr *valueObject.ServiceType
+	if untrustedInput["type"] != nil {
+		svcType, err := valueObject.NewServiceType(untrustedInput["type"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		typePtr = &svcType
+	}
+
+	var startCmdPtr *valueObject.UnixCommand
+	if untrustedInput["startCmd"] != nil {
+		startCmd, err := valueObject.NewUnixCommand(untrustedInput["startCmd"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		startCmdPtr = &startCmd
+	}
+
+	var statusPtr *valueObject.ServiceStatus
+	if untrustedInput["status"] != nil {
+		status, err := valueObject.NewServiceStatus(untrustedInput["status"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		statusPtr = &status
+	}
+
+	var versionPtr *valueObject.ServiceVersion
+	if untrustedInput["version"] != nil {
+		version, err := valueObject.NewServiceVersion(untrustedInput["version"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		versionPtr = &version
+	}
+
+	envs := []valueObject.ServiceEnv{}
+	if untrustedInput["envs"] != nil {
+		rawEnvs, assertOk := untrustedInput["envs"].([]string)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "EnvsMustBeStringArray")
+		}
+
+		for _, rawEnv := range rawEnvs {
+			env, err := valueObject.NewServiceEnv(rawEnv)
+			if err != nil {
+				slog.Debug(err.Error(), slog.String("env", rawEnv))
+				continue
+			}
+			envs = append(envs, env)
+		}
+	}
+
+	portBindings := []valueObject.PortBinding{}
+	if _, exists := untrustedInput["portBindings"]; exists {
+		rawPortBindings, assertOk := untrustedInput["portBindings"].([]string)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "PortBindingsMustBeStringArray")
+		}
+
+		for _, rawPortBinding := range rawPortBindings {
+			if len(rawPortBinding) == 0 {
+				continue
+			}
+
+			portBinding, err := valueObject.NewPortBinding(rawPortBinding)
+			if err != nil {
+				slog.Debug(err.Error(), slog.String("portBinding", rawPortBinding))
+				continue
+			}
+			portBindings = append(portBindings, portBinding)
+		}
+	}
+
+	var startupFilePtr *valueObject.UnixFilePath
+	if untrustedInput["startupFile"] != nil {
+		startupFile, err := valueObject.NewUnixFilePath(untrustedInput["startupFile"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		startupFilePtr = &startupFile
+	}
+
+	var autoStartPtr *bool
+	if untrustedInput["autoStart"] != nil {
+		autoStart, err := voHelper.InterfaceToBool(untrustedInput["autoStart"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		autoStartPtr = &autoStart
+	}
+
+	var autoRestartPtr *bool
+	if untrustedInput["autoRestart"] != nil {
+		autoRestart, err := voHelper.InterfaceToBool(untrustedInput["autoRestart"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		autoRestartPtr = &autoRestart
+	}
+
+	var timeoutStartSecsPtr *uint
+	if untrustedInput["timeoutStartSecs"] != nil {
+		timeoutStartSecs, err := voHelper.InterfaceToUint(untrustedInput["timeoutStartSecs"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		timeoutStartSecsPtr = &timeoutStartSecs
+	}
+
+	var maxStartRetriesPtr *uint
+	if untrustedInput["maxStartRetries"] != nil {
+		maxStartRetries, err := voHelper.InterfaceToUint(untrustedInput["maxStartRetries"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		maxStartRetriesPtr = &maxStartRetries
+	}
+
+	var logOutputPathPtr *valueObject.UnixFilePath
+	if untrustedInput["logOutputPath"] != nil {
+		logOutputPath, err := valueObject.NewUnixFilePath(untrustedInput["logOutputPath"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		logOutputPathPtr = &logOutputPath
+	}
+
+	var logErrorPathPtr *valueObject.UnixFilePath
+	if untrustedInput["logErrorPath"] != nil {
+		logErrorPath, err := valueObject.NewUnixFilePath(untrustedInput["logErrorPath"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		logErrorPathPtr = &logErrorPath
+	}
+
+	var avatarUrlPtr *valueObject.Url
+	if untrustedInput["avatarUrl"] != nil {
+		avatarUrl, err := valueObject.NewUrl(untrustedInput["avatarUrl"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		avatarUrlPtr = &avatarUrl
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	updateDto := dto.NewUpdateService(
+		name, typePtr, versionPtr, statusPtr, startCmdPtr, envs, portBindings, nil,
+		nil, nil, nil, nil, nil, nil, startupFilePtr, autoStartPtr, autoRestartPtr,
+		timeoutStartSecsPtr, maxStartRetriesPtr, logOutputPathPtr, logErrorPathPtr,
+		avatarUrlPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.UpdateService(
+		liaison.servicesQueryRepo, liaison.servicesCmdRepo, liaison.mappingQueryRepo,
+		liaison.mappingCmdRepo, liaison.activityRecordCmdRepo, updateDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "ServiceUpdated")
+}
+
+func (liaison *ServicesLiaison) Delete(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"name"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	name, err := valueObject.NewServiceName(untrustedInput["name"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteService(name, operatorAccountId, operatorIpAddress)
+
+	err = useCase.DeleteService(
+		liaison.servicesQueryRepo, liaison.servicesCmdRepo, liaison.mappingQueryRepo,
+		liaison.mappingCmdRepo, liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "ServiceDeleted")
+}

+ 313 - 0
src/presentation/liaison/ssl.go

@@ -0,0 +1,313 @@
+package liaison
+
+import (
+	"errors"
+	"strings"
+
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/entity"
+	"github.com/goinfinite/os/src/domain/useCase"
+	"github.com/goinfinite/os/src/domain/valueObject"
+	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
+	infraEnvs "github.com/goinfinite/os/src/infra/envs"
+	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
+	scheduledTaskInfra "github.com/goinfinite/os/src/infra/scheduledTask"
+	sslInfra "github.com/goinfinite/os/src/infra/ssl"
+	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
+)
+
+type SslLiaison struct {
+	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
+	sslQueryRepo          *sslInfra.SslQueryRepo
+	sslCmdRepo            *sslInfra.SslCmdRepo
+	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
+}
+
+func NewSslLiaison(
+	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
+	transientDbSvc *internalDbInfra.TransientDatabaseService,
+	trailDbSvc *internalDbInfra.TrailDatabaseService,
+) *SslLiaison {
+	return &SslLiaison{
+		persistentDbSvc:       persistentDbSvc,
+		sslQueryRepo:          sslInfra.NewSslQueryRepo(),
+		sslCmdRepo:            sslInfra.NewSslCmdRepo(persistentDbSvc, transientDbSvc),
+		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
+	}
+}
+
+func (liaison *SslLiaison) SslPairReadRequestFactory(
+	untrustedInput map[string]any,
+	withMappings bool,
+) (readRequestDto dto.ReadSslPairsRequest, err error) {
+	if untrustedInput["sslPairId"] == nil && untrustedInput["id"] != nil {
+		untrustedInput["sslPairId"] = untrustedInput["id"]
+	}
+
+	var sslPairIdPtr *valueObject.SslPairId
+	if untrustedInput["sslPairId"] != nil {
+		sslPairId, err := valueObject.NewSslPairId(untrustedInput["sslPairId"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		sslPairIdPtr = &sslPairId
+	}
+
+	if untrustedInput["virtualHostHostname"] == nil && untrustedInput["hostname"] != nil {
+		untrustedInput["virtualHostHostname"] = untrustedInput["hostname"]
+	}
+
+	var vhostHostnamePtr *valueObject.Fqdn
+	if untrustedInput["virtualHostHostname"] != nil {
+		vhostHostname, err := valueObject.NewFqdn(untrustedInput["virtualHostHostname"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		vhostHostnamePtr = &vhostHostname
+	}
+
+	altNames := []valueObject.SslHostname{}
+	if untrustedInput["altNames"] != nil {
+		var assertOk bool
+		altNames, assertOk = untrustedInput["altNames"].([]valueObject.SslHostname)
+		if !assertOk {
+			return readRequestDto, errors.New("InvalidAltNamesStructure")
+		}
+	}
+
+	timeParamNames := []string{
+		"issuedBeforeAt", "issuedAfterAt", "expiresBeforeAt", "expiresAfterAt",
+	}
+	timeParamPtrs := liaisonHelper.TimeParamsParser(timeParamNames, untrustedInput)
+
+	requestPagination, err := liaisonHelper.PaginationParser(
+		untrustedInput, useCase.SslPairsDefaultPagination,
+	)
+	if err != nil {
+		return readRequestDto, err
+	}
+
+	return dto.ReadSslPairsRequest{
+		Pagination:          requestPagination,
+		SslPairId:           sslPairIdPtr,
+		VirtualHostHostname: vhostHostnamePtr,
+		AltNames:            altNames,
+		IssuedBeforeAt:      timeParamPtrs["issuedBeforeAt"],
+		IssuedAfterAt:       timeParamPtrs["issuedAfterAt"],
+		ExpiresBeforeAt:     timeParamPtrs["expiresBeforeAt"],
+		ExpiresAfterAt:      timeParamPtrs["expiresAfterAt"],
+	}, nil
+}
+
+func (liaison *SslLiaison) Read(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	readRequestDto, err := liaison.SslPairReadRequestFactory(untrustedInput, false)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	readResponseDto, err := useCase.ReadSslPairs(liaison.sslQueryRepo, readRequestDto)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, readResponseDto)
+}
+
+func (liaison *SslLiaison) Create(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"virtualHostsHostnames", "certificate", "key"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	vhostHostnames, assertOk := untrustedInput["virtualHostsHostnames"].([]valueObject.Fqdn)
+	if !assertOk {
+		return NewLiaisonOutput(UserError, errors.New("InvalidVirtualHostsStructure"))
+	}
+
+	certContent, err := valueObject.NewSslCertificateContent(untrustedInput["certificate"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+	certEntity, err := entity.NewSslCertificate(certContent)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var chainCertsPtr *entity.SslCertificate
+	if untrustedInput["chainCertificates"] != nil {
+		chainCertContent, err := valueObject.NewSslCertificateContent(untrustedInput["chainCertificates"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("SslCertificateChainContentError"))
+		}
+		chainCertEntity, err := entity.NewSslCertificate(chainCertContent)
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("SslCertificateChainParseError"))
+		}
+		chainCertsPtr = &chainCertEntity
+	}
+
+	privateKeyContent, err := valueObject.NewSslPrivateKey(untrustedInput["key"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateSslPair(
+		vhostHostnames, certEntity, chainCertsPtr, privateKeyContent,
+		operatorAccountId, operatorIpAddress,
+	)
+
+	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(liaison.persistentDbSvc)
+
+	err = useCase.CreateSslPair(
+		vhostQueryRepo, liaison.sslCmdRepo, liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "SslPairCreated")
+}
+
+func (liaison *SslLiaison) CreatePubliclyTrusted(
+	untrustedInput map[string]any,
+	shouldSchedule bool,
+) LiaisonOutput {
+	if untrustedInput["hostname"] != nil && untrustedInput["virtualHostHostname"] == nil {
+		untrustedInput["virtualHostHostname"] = untrustedInput["hostname"]
+	}
+
+	if untrustedInput["vhostHostname"] != nil && untrustedInput["virtualHostHostname"] == nil {
+		untrustedInput["virtualHostHostname"] = untrustedInput["vhostHostname"]
+	}
+
+	requiredParams := []string{"virtualHostHostname"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	vhostHostname, err := valueObject.NewFqdn(untrustedInput["virtualHostHostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	if shouldSchedule {
+		cliCmd := infraEnvs.InfiniteOsBinary + " ssl create-trusted"
+		installParams := []string{
+			"--hostname", vhostHostname.String(),
+		}
+		cliCmd += " " + strings.Join(installParams, " ")
+
+		scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(liaison.persistentDbSvc)
+		taskName, _ := valueObject.NewScheduledTaskName("CreatePubliclyTrustedSslPair")
+		taskCmd, _ := valueObject.NewUnixCommand(cliCmd)
+		taskTag, _ := valueObject.NewScheduledTaskTag("ssl")
+		taskTags := []valueObject.ScheduledTaskTag{taskTag}
+		timeoutSecs := uint16(1800)
+
+		scheduledTaskCreateDto := dto.NewCreateScheduledTask(
+			taskName, taskCmd, taskTags, &timeoutSecs, nil,
+		)
+
+		err = useCase.CreateScheduledTask(scheduledTaskCmdRepo, scheduledTaskCreateDto)
+		if err != nil {
+			return NewLiaisonOutput(InfraError, err.Error())
+		}
+
+		return NewLiaisonOutput(Created, "PubliclyTrustedSslPairCreationScheduled")
+	}
+
+	createDto := dto.NewCreatePubliclyTrustedSslPair(
+		vhostHostname, operatorAccountId, operatorIpAddress,
+	)
+
+	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(liaison.persistentDbSvc)
+
+	_, err = useCase.CreatePubliclyTrustedSslPair(
+		vhostQueryRepo, liaison.sslCmdRepo, liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "PubliclyTrustedSslPairCreated")
+}
+
+func (liaison *SslLiaison) Delete(untrustedInput map[string]any) LiaisonOutput {
+	if untrustedInput["id"] == nil && untrustedInput["sslPairId"] != nil {
+		untrustedInput["id"] = untrustedInput["sslPairId"]
+	}
+
+	requiredParams := []string{"id"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	pairId, err := valueObject.NewSslPairId(untrustedInput["id"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	err = useCase.DeleteSslPair(
+		liaison.sslQueryRepo, liaison.sslCmdRepo, liaison.activityRecordCmdRepo,
+		dto.NewDeleteSslPair(pairId, operatorAccountId, operatorIpAddress),
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "SslPairDeleted")
+}

+ 1012 - 0
src/presentation/liaison/virtualHost.go

@@ -0,0 +1,1012 @@
+package liaison
+
+import (
+	"errors"
+
+	"github.com/goinfinite/os/src/domain/dto"
+	"github.com/goinfinite/os/src/domain/useCase"
+	"github.com/goinfinite/os/src/domain/valueObject"
+	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
+	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
+	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
+	servicesInfra "github.com/goinfinite/os/src/infra/services"
+	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
+	liaisonHelper "github.com/goinfinite/os/src/presentation/liaison/helper"
+
+	tkValueObject "github.com/goinfinite/tk/src/domain/valueObject"
+	tkVoUtil "github.com/goinfinite/tk/src/domain/valueObject/util"
+)
+
+type VirtualHostLiaison struct {
+	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
+	trailDbSvc            *internalDbInfra.TrailDatabaseService
+	vhostQueryRepo        *vhostInfra.VirtualHostQueryRepo
+	vhostCmdRepo          *vhostInfra.VirtualHostCmdRepo
+	mappingQueryRepo      *vhostInfra.MappingQueryRepo
+	mappingCmdRepo        *vhostInfra.MappingCmdRepo
+	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
+}
+
+func NewVirtualHostLiaison(
+	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
+	trailDbSvc *internalDbInfra.TrailDatabaseService,
+) *VirtualHostLiaison {
+	return &VirtualHostLiaison{
+		persistentDbSvc:       persistentDbSvc,
+		trailDbSvc:            trailDbSvc,
+		vhostQueryRepo:        vhostInfra.NewVirtualHostQueryRepo(persistentDbSvc),
+		vhostCmdRepo:          vhostInfra.NewVirtualHostCmdRepo(persistentDbSvc),
+		mappingQueryRepo:      vhostInfra.NewMappingQueryRepo(persistentDbSvc),
+		mappingCmdRepo:        vhostInfra.NewMappingCmdRepo(persistentDbSvc),
+		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
+	}
+}
+
+func (liaison *VirtualHostLiaison) VirtualHostReadRequestFactory(
+	untrustedInput map[string]any,
+	withMappings bool,
+) (readRequestDto dto.ReadVirtualHostsRequest, err error) {
+	var hostnamePtr *valueObject.Fqdn
+	if untrustedInput["hostname"] != nil {
+		hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		hostnamePtr = &hostname
+	}
+
+	var typePtr *valueObject.VirtualHostType
+	if untrustedInput["type"] != nil {
+		vhostType, err := valueObject.NewVirtualHostType(untrustedInput["type"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		typePtr = &vhostType
+	}
+
+	var rootDirectoryPtr *valueObject.UnixFilePath
+	if untrustedInput["rootDirectory"] != nil {
+		rootDirectory, err := valueObject.NewUnixFilePath(untrustedInput["rootDirectory"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		rootDirectoryPtr = &rootDirectory
+	}
+
+	var parentHostnamePtr *valueObject.Fqdn
+	if untrustedInput["parentHostname"] != nil {
+		parentHostname, err := valueObject.NewFqdn(untrustedInput["parentHostname"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		parentHostnamePtr = &parentHostname
+	}
+
+	if untrustedInput["withMappings"] != nil {
+		withMappings, err = voHelper.InterfaceToBool(untrustedInput["withMappings"])
+		if err != nil {
+			return readRequestDto, err
+		}
+	}
+
+	timeParamNames := []string{"createdBeforeAt", "createdAfterAt"}
+	timeParamPtrs := liaisonHelper.TimeParamsParser(timeParamNames, untrustedInput)
+
+	requestPagination, err := liaisonHelper.PaginationParser(
+		untrustedInput, useCase.VirtualHostsDefaultPagination,
+	)
+	if err != nil {
+		return readRequestDto, err
+	}
+
+	return dto.ReadVirtualHostsRequest{
+		Pagination:      requestPagination,
+		Hostname:        hostnamePtr,
+		VirtualHostType: typePtr,
+		RootDirectory:   rootDirectoryPtr,
+		ParentHostname:  parentHostnamePtr,
+		WithMappings:    &withMappings,
+		CreatedBeforeAt: timeParamPtrs["createdBeforeAt"],
+		CreatedAfterAt:  timeParamPtrs["createdAfterAt"],
+	}, nil
+}
+
+func (liaison *VirtualHostLiaison) Read(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	readRequestDto, err := liaison.VirtualHostReadRequestFactory(untrustedInput, false)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	readResponseDto, err := useCase.ReadVirtualHosts(liaison.vhostQueryRepo, readRequestDto)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, readResponseDto)
+}
+
+func (liaison *VirtualHostLiaison) Create(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"hostname"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	vhostType := valueObject.VirtualHostTypeTopLevel
+	if untrustedInput["type"] != nil {
+		vhostType, err = valueObject.NewVirtualHostType(untrustedInput["type"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	isWildcard := false
+	if untrustedInput["isWildcard"] != nil {
+		isWildcard, err = voHelper.InterfaceToBool(untrustedInput["isWildcard"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	var parentHostnamePtr *valueObject.Fqdn
+	if untrustedInput["parentHostname"] != nil {
+		parentHostname, err := valueObject.NewFqdn(untrustedInput["parentHostname"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		parentHostnamePtr = &parentHostname
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateVirtualHost(
+		hostname, vhostType, &isWildcard, parentHostnamePtr,
+		operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.CreateVirtualHost(
+		liaison.vhostQueryRepo, liaison.vhostCmdRepo, liaison.activityRecordCmdRepo,
+		createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "VirtualHostCreated")
+}
+
+func (liaison *VirtualHostLiaison) Update(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"hostname"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var isWildcardPtr *bool
+	if untrustedInput["isWildcard"] != nil {
+		isWildcard, err := voHelper.InterfaceToBool(untrustedInput["isWildcard"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidIsWildcard"))
+		}
+		isWildcardPtr = &isWildcard
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	updateDto := dto.NewUpdateVirtualHost(
+		hostname, isWildcardPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.UpdateVirtualHost(
+		liaison.vhostQueryRepo, liaison.vhostCmdRepo, liaison.activityRecordCmdRepo,
+		updateDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "VirtualHostUpdated")
+}
+
+func (liaison *VirtualHostLiaison) Delete(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"hostname"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteVirtualHost(hostname, operatorAccountId, operatorIpAddress)
+	err = useCase.DeleteVirtualHost(
+		liaison.vhostQueryRepo, liaison.vhostCmdRepo,
+		liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "VirtualHostDeleted")
+}
+
+func (liaison *VirtualHostLiaison) ReadWithMappings(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	readRequestDto, err := liaison.VirtualHostReadRequestFactory(untrustedInput, true)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	readResponseDto, err := useCase.ReadVirtualHosts(liaison.vhostQueryRepo, readRequestDto)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, readResponseDto)
+}
+
+func (liaison *VirtualHostLiaison) CreateMapping(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"hostname", "path", "targetType"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	hostname, err := valueObject.NewFqdn(untrustedInput["hostname"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	path, err := valueObject.NewMappingPath(untrustedInput["path"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	matchPattern := valueObject.MappingMatchPatternBeginsWith
+	if untrustedInput["matchPattern"] != nil {
+		matchPattern, err = valueObject.NewMappingMatchPattern(untrustedInput["matchPattern"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	targetType, err := valueObject.NewMappingTargetType(untrustedInput["targetType"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var targetValuePtr *valueObject.MappingTargetValue
+	if untrustedInput["targetValue"] != nil {
+		targetValue, err := valueObject.NewMappingTargetValue(
+			untrustedInput["targetValue"], targetType,
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		targetValuePtr = &targetValue
+	}
+
+	var targetHttpResponseCodePtr *valueObject.HttpResponseCode
+	if untrustedInput["targetHttpResponseCode"] != nil {
+		if untrustedInput["targetHttpResponseCode"] == "" {
+			untrustedInput["targetHttpResponseCode"] = 301
+		}
+		targetHttpResponseCode, err := valueObject.NewHttpResponseCode(
+			untrustedInput["targetHttpResponseCode"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		targetHttpResponseCodePtr = &targetHttpResponseCode
+	}
+
+	var shouldUpgradeInsecureRequestsPtr *bool
+	if untrustedInput["shouldUpgradeInsecureRequests"] != nil {
+		shouldUpgradeInsecureRequests, err := tkVoUtil.InterfaceToBool(
+			untrustedInput["shouldUpgradeInsecureRequests"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidShouldUpgradeInsecureRequests")
+		}
+		shouldUpgradeInsecureRequestsPtr = &shouldUpgradeInsecureRequests
+	}
+
+	var mappingSecurityRuleIdPtr *valueObject.MappingSecurityRuleId
+	if untrustedInput["mappingSecurityRuleId"] != nil && untrustedInput["mappingSecurityRuleId"] != "" {
+		mappingSecurityRuleId, err := valueObject.NewMappingSecurityRuleId(
+			untrustedInput["mappingSecurityRuleId"],
+		)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		mappingSecurityRuleIdPtr = &mappingSecurityRuleId
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateMapping(
+		hostname, path, matchPattern, targetType, targetValuePtr,
+		targetHttpResponseCodePtr, shouldUpgradeInsecureRequestsPtr,
+		mappingSecurityRuleIdPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	servicesQueryRepo := servicesInfra.NewServicesQueryRepo(liaison.persistentDbSvc)
+
+	err = useCase.CreateMapping(
+		liaison.vhostQueryRepo, liaison.mappingCmdRepo, servicesQueryRepo,
+		liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "MappingCreated")
+}
+
+func (liaison *VirtualHostLiaison) DeleteMapping(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	if untrustedInput["mappingId"] == nil && untrustedInput["id"] != nil {
+		untrustedInput["mappingId"] = untrustedInput["id"]
+	}
+
+	requiredParams := []string{"mappingId"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	id, err := valueObject.NewMappingId(untrustedInput["mappingId"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteMapping(id, operatorAccountId, operatorIpAddress)
+	err = useCase.DeleteMapping(
+		liaison.mappingQueryRepo, liaison.mappingCmdRepo,
+		liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, "MappingDeleted")
+}
+
+func (liaison *VirtualHostLiaison) UpdateMapping(untrustedInput map[string]any) LiaisonOutput {
+	requiredParams := []string{"id"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	id, err := valueObject.NewMappingId(untrustedInput["id"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var pathPtr *valueObject.MappingPath
+	if untrustedInput["path"] != nil {
+		path, err := valueObject.NewMappingPath(untrustedInput["path"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		pathPtr = &path
+	}
+
+	var matchPatternPtr *valueObject.MappingMatchPattern
+	if untrustedInput["matchPattern"] != nil {
+		matchPattern, err := valueObject.NewMappingMatchPattern(untrustedInput["matchPattern"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		matchPatternPtr = &matchPattern
+	}
+
+	var targetTypePtr *valueObject.MappingTargetType
+	if untrustedInput["targetType"] != nil {
+		targetType, err := valueObject.NewMappingTargetType(untrustedInput["targetType"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		targetTypePtr = &targetType
+	}
+
+	var targetValuePtr *valueObject.MappingTargetValue
+	if untrustedInput["targetValue"] != nil {
+		if targetTypePtr == nil {
+			mappingEntity, err := liaison.mappingQueryRepo.ReadFirst(
+				dto.ReadMappingsRequest{MappingId: &id},
+			)
+			if err != nil {
+				return NewLiaisonOutput(InfraError, "ReadMappingEntityToRetrieveTargetTypeError")
+			}
+			targetTypePtr = &mappingEntity.TargetType
+		}
+
+		targetValue, err := valueObject.NewMappingTargetValue(untrustedInput["targetValue"], *targetTypePtr)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		targetValuePtr = &targetValue
+	}
+
+	var targetHttpResponseCodePtr *valueObject.HttpResponseCode
+	if untrustedInput["targetHttpResponseCode"] != nil {
+		targetHttpResponseCode, err := valueObject.NewHttpResponseCode(untrustedInput["targetHttpResponseCode"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		targetHttpResponseCodePtr = &targetHttpResponseCode
+	}
+
+	var shouldUpgradeInsecureRequestsPtr *bool
+	if untrustedInput["shouldUpgradeInsecureRequests"] != nil {
+		shouldUpgradeInsecureRequests, err := tkVoUtil.InterfaceToBool(untrustedInput["shouldUpgradeInsecureRequests"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, errors.New("InvalidShouldUpgradeInsecureRequests"))
+		}
+		shouldUpgradeInsecureRequestsPtr = &shouldUpgradeInsecureRequests
+	}
+
+	clearableFields := []string{}
+	var mappingSecurityRuleIdPtr *valueObject.MappingSecurityRuleId
+	switch mappingSecurityRuleIdValue := untrustedInput["mappingSecurityRuleId"]; {
+	case mappingSecurityRuleIdValue == nil:
+	case mappingSecurityRuleIdValue == "" || mappingSecurityRuleIdValue == " ":
+		clearableFields = append(clearableFields, "mappingSecurityRuleId")
+	default:
+		mappingSecurityRuleId, err := valueObject.NewMappingSecurityRuleId(mappingSecurityRuleIdValue)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		mappingSecurityRuleIdPtr = &mappingSecurityRuleId
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	updateDto := dto.NewUpdateMapping(
+		id, pathPtr, matchPatternPtr, targetTypePtr, targetValuePtr,
+		targetHttpResponseCodePtr, shouldUpgradeInsecureRequestsPtr,
+		mappingSecurityRuleIdPtr, clearableFields,
+		operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.UpdateMapping(
+		liaison.mappingQueryRepo, liaison.mappingCmdRepo,
+		liaison.activityRecordCmdRepo, updateDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "MappingUpdated")
+}
+
+func (liaison *VirtualHostLiaison) MappingSecurityRuleReadRequestFactory(
+	untrustedInput map[string]any,
+) (readRequestDto dto.ReadMappingSecurityRulesRequest, err error) {
+	var mappingSecurityRuleIdPtr *valueObject.MappingSecurityRuleId
+	if untrustedInput["id"] != nil {
+		id, err := valueObject.NewMappingSecurityRuleId(untrustedInput["id"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		mappingSecurityRuleIdPtr = &id
+	}
+
+	var mappingSecurityRuleNamePtr *valueObject.MappingSecurityRuleName
+	if untrustedInput["name"] != nil {
+		name, err := valueObject.NewMappingSecurityRuleName(untrustedInput["name"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		mappingSecurityRuleNamePtr = &name
+	}
+
+	var allowedIpPtr *tkValueObject.CidrBlock
+	if untrustedInput["allowedIp"] != nil {
+		allowedIp, err := tkValueObject.NewCidrBlock(untrustedInput["allowedIp"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		allowedIpPtr = &allowedIp
+	}
+
+	var blockedIpPtr *tkValueObject.CidrBlock
+	if untrustedInput["blockedIp"] != nil {
+		blockedIp, err := tkValueObject.NewCidrBlock(untrustedInput["blockedIp"])
+		if err != nil {
+			return readRequestDto, err
+		}
+		blockedIpPtr = &blockedIp
+	}
+
+	timeParamNames := []string{"createdBeforeAt", "createdAfterAt"}
+	timeParamPtrs := liaisonHelper.TimeParamsParser(timeParamNames, untrustedInput)
+
+	requestPagination, err := liaisonHelper.PaginationParser(
+		untrustedInput, useCase.MappingSecurityRulesDefaultPagination,
+	)
+	if err != nil {
+		return readRequestDto, err
+	}
+
+	return dto.ReadMappingSecurityRulesRequest{
+		Pagination:              requestPagination,
+		MappingSecurityRuleId:   mappingSecurityRuleIdPtr,
+		MappingSecurityRuleName: mappingSecurityRuleNamePtr,
+		AllowedIp:               allowedIpPtr,
+		BlockedIp:               blockedIpPtr,
+		CreatedBeforeAt:         timeParamPtrs["createdBeforeAt"],
+		CreatedAfterAt:          timeParamPtrs["createdAfterAt"],
+	}, nil
+}
+
+func (liaison *VirtualHostLiaison) ReadMappingSecurityRules(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	readRequestDto, err := liaison.MappingSecurityRuleReadRequestFactory(untrustedInput)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	readResponseDto, err := useCase.ReadMappingSecurityRules(
+		liaison.mappingQueryRepo, readRequestDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, readResponseDto)
+}
+
+func (liaison *VirtualHostLiaison) CreateMappingSecurityRule(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"name"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	name, err := valueObject.NewMappingSecurityRuleName(untrustedInput["name"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var descriptionPtr *valueObject.MappingSecurityRuleDescription
+	if untrustedInput["description"] != nil {
+		description, err := valueObject.NewMappingSecurityRuleDescription(untrustedInput["description"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		descriptionPtr = &description
+	}
+
+	allowedIps := []tkValueObject.CidrBlock{}
+	if untrustedInput["allowedIps"] != nil {
+		allowedIpsInput, assertOk := untrustedInput["allowedIps"].([]tkValueObject.CidrBlock)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidAllowedIps")
+		}
+		allowedIps = allowedIpsInput
+	}
+
+	blockedIps := []tkValueObject.CidrBlock{}
+	if untrustedInput["blockedIps"] != nil {
+		blockedIpsInput, assertOk := untrustedInput["blockedIps"].([]tkValueObject.CidrBlock)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidBlockedIps")
+		}
+		blockedIps = blockedIpsInput
+	}
+
+	var rpsSoftLimitPerIpPtr *uint
+	if untrustedInput["rpsSoftLimitPerIp"] != nil && untrustedInput["rpsSoftLimitPerIp"] != "" {
+		softLimit, err := tkVoUtil.InterfaceToUint(untrustedInput["rpsSoftLimitPerIp"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidRpsSoftLimitPerIp")
+		}
+		rpsSoftLimitPerIpPtr = &softLimit
+	}
+
+	var rpsHardLimitPerIpPtr *uint
+	if untrustedInput["rpsHardLimitPerIp"] != nil && untrustedInput["rpsHardLimitPerIp"] != "" {
+		hardLimit, err := tkVoUtil.InterfaceToUint(untrustedInput["rpsHardLimitPerIp"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidRpsHardLimitPerIp")
+		}
+		rpsHardLimitPerIpPtr = &hardLimit
+	}
+
+	var responseCodeOnMaxRequestsPtr *uint
+	if untrustedInput["responseCodeOnMaxRequests"] != nil && untrustedInput["responseCodeOnMaxRequests"] != "" {
+		responseCode, err := tkVoUtil.InterfaceToUint(untrustedInput["responseCodeOnMaxRequests"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidResponseCodeOnMaxRequests")
+		}
+		responseCodeOnMaxRequestsPtr = &responseCode
+	}
+
+	var maxConnectionsPerIpPtr *uint
+	if untrustedInput["maxConnectionsPerIp"] != nil && untrustedInput["maxConnectionsPerIp"] != "" {
+		maxConns, err := tkVoUtil.InterfaceToUint(untrustedInput["maxConnectionsPerIp"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidMaxConnectionsPerIp")
+		}
+		maxConnectionsPerIpPtr = &maxConns
+	}
+
+	var bandwidthBpsLimitPerConnectionPtr *valueObject.Byte
+	if untrustedInput["bandwidthBpsLimitPerConnection"] != nil && untrustedInput["bandwidthBpsLimitPerConnection"] != "" {
+		bandwidthBpsLimit, err := valueObject.NewByte(untrustedInput["bandwidthBpsLimitPerConnection"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidBandwidthBpsLimitPerConnection")
+		}
+		bandwidthBpsLimitPerConnectionPtr = &bandwidthBpsLimit
+	}
+
+	var bandwidthLimitOnlyAfterBytesPtr *valueObject.Byte
+	if untrustedInput["bandwidthLimitOnlyAfterBytes"] != nil && untrustedInput["bandwidthLimitOnlyAfterBytes"] != "" {
+		bandwidthLimitOnlyAfterBytes, err := valueObject.NewByte(untrustedInput["bandwidthLimitOnlyAfterBytes"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidBandwidthLimitOnlyAfterBytes")
+		}
+		bandwidthLimitOnlyAfterBytesPtr = &bandwidthLimitOnlyAfterBytes
+	}
+
+	var responseCodeOnMaxConnectionsPtr *uint
+	if untrustedInput["responseCodeOnMaxConnections"] != nil && untrustedInput["responseCodeOnMaxConnections"] != "" {
+		responseCode, err := tkVoUtil.InterfaceToUint(untrustedInput["responseCodeOnMaxConnections"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidResponseCodeOnMaxConnections")
+		}
+		responseCodeOnMaxConnectionsPtr = &responseCode
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	createDto := dto.NewCreateMappingSecurityRule(
+		name, descriptionPtr, allowedIps, blockedIps, rpsSoftLimitPerIpPtr,
+		rpsHardLimitPerIpPtr, responseCodeOnMaxRequestsPtr, maxConnectionsPerIpPtr,
+		bandwidthBpsLimitPerConnectionPtr, bandwidthLimitOnlyAfterBytesPtr,
+		responseCodeOnMaxConnectionsPtr, operatorAccountId, operatorIpAddress,
+	)
+
+	mappingSecurityRuleId, err := useCase.CreateMappingSecurityRule(
+		liaison.mappingQueryRepo, liaison.mappingCmdRepo,
+		liaison.activityRecordCmdRepo, createDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Created, map[string]interface{}{
+		"id": mappingSecurityRuleId.Uint64(),
+	})
+}
+
+func (liaison *VirtualHostLiaison) UpdateMappingSecurityRule(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"id"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	id, err := valueObject.NewMappingSecurityRuleId(untrustedInput["id"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	var namePtr *valueObject.MappingSecurityRuleName
+	if untrustedInput["name"] != nil {
+		name, err := valueObject.NewMappingSecurityRuleName(untrustedInput["name"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		namePtr = &name
+	}
+
+	clearableFields := []string{}
+
+	var descriptionPtr *valueObject.MappingSecurityRuleDescription
+	switch descriptionValue := untrustedInput["description"]; {
+	case descriptionValue == nil:
+	case descriptionValue == "" || descriptionValue == " ":
+		clearableFields = append(clearableFields, "description")
+	default:
+		description, err := valueObject.NewMappingSecurityRuleDescription(descriptionValue)
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+		descriptionPtr = &description
+	}
+
+	allowedIps := []tkValueObject.CidrBlock{}
+	if untrustedInput["allowedIps"] != nil {
+		var assertOk bool
+		allowedIps, assertOk = untrustedInput["allowedIps"].([]tkValueObject.CidrBlock)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidAllowedIps")
+		}
+		if len(allowedIps) == 0 {
+			clearableFields = append(clearableFields, "allowedIps")
+		}
+	}
+
+	blockedIps := []tkValueObject.CidrBlock{}
+	if untrustedInput["blockedIps"] != nil {
+		var assertOk bool
+		blockedIps, assertOk = untrustedInput["blockedIps"].([]tkValueObject.CidrBlock)
+		if !assertOk {
+			return NewLiaisonOutput(UserError, "InvalidBlockedIps")
+		}
+		if len(blockedIps) == 0 {
+			clearableFields = append(clearableFields, "blockedIps")
+		}
+	}
+
+	var rpsSoftLimitPerIpPtr *uint
+	if untrustedInput["rpsSoftLimitPerIp"] != nil && untrustedInput["rpsSoftLimitPerIp"] != "" {
+		softLimit, err := tkVoUtil.InterfaceToUint(untrustedInput["rpsSoftLimitPerIp"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidRpsSoftLimitPerIp")
+		}
+		rpsSoftLimitPerIpPtr = &softLimit
+	}
+
+	var rpsHardLimitPerIpPtr *uint
+	if untrustedInput["rpsHardLimitPerIp"] != nil && untrustedInput["rpsHardLimitPerIp"] != "" {
+		hardLimit, err := tkVoUtil.InterfaceToUint(untrustedInput["rpsHardLimitPerIp"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidRpsHardLimitPerIp")
+		}
+		rpsHardLimitPerIpPtr = &hardLimit
+	}
+
+	var responseCodeOnMaxRequestsPtr *uint
+	if untrustedInput["responseCodeOnMaxRequests"] != nil && untrustedInput["responseCodeOnMaxRequests"] != "" {
+		responseCode, err := tkVoUtil.InterfaceToUint(untrustedInput["responseCodeOnMaxRequests"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidResponseCodeOnMaxRequests")
+		}
+		responseCodeOnMaxRequestsPtr = &responseCode
+	}
+
+	var maxConnectionsPerIpPtr *uint
+	if untrustedInput["maxConnectionsPerIp"] != nil && untrustedInput["maxConnectionsPerIp"] != "" {
+		maxConns, err := tkVoUtil.InterfaceToUint(untrustedInput["maxConnectionsPerIp"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidMaxConnectionsPerIp")
+		}
+		maxConnectionsPerIpPtr = &maxConns
+	}
+
+	var bandwidthBpsLimitPerConnectionPtr *valueObject.Byte
+	if untrustedInput["bandwidthBpsLimitPerConnection"] != nil && untrustedInput["bandwidthBpsLimitPerConnection"] != "" {
+		bandwidthBpsLimit, err := valueObject.NewByte(untrustedInput["bandwidthBpsLimitPerConnection"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidBandwidthBpsLimitPerConnection")
+		}
+		bandwidthBpsLimitPerConnectionPtr = &bandwidthBpsLimit
+	}
+
+	var bandwidthLimitOnlyAfterBytesPtr *valueObject.Byte
+	if untrustedInput["bandwidthLimitOnlyAfterBytes"] != nil && untrustedInput["bandwidthLimitOnlyAfterBytes"] != "" {
+		bandwidthLimitOnlyAfterBytes, err := valueObject.NewByte(untrustedInput["bandwidthLimitOnlyAfterBytes"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidBandwidthLimitOnlyAfterBytes")
+		}
+		bandwidthLimitOnlyAfterBytesPtr = &bandwidthLimitOnlyAfterBytes
+	}
+
+	var responseCodeOnMaxConnectionsPtr *uint
+	if untrustedInput["responseCodeOnMaxConnections"] != nil && untrustedInput["responseCodeOnMaxConnections"] != "" {
+		responseCode, err := tkVoUtil.InterfaceToUint(untrustedInput["responseCodeOnMaxConnections"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, "InvalidResponseCodeOnMaxConnections")
+		}
+		responseCodeOnMaxConnectionsPtr = &responseCode
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	updateDto := dto.NewUpdateMappingSecurityRule(
+		id, namePtr, descriptionPtr, allowedIps, blockedIps,
+		rpsSoftLimitPerIpPtr, rpsHardLimitPerIpPtr, responseCodeOnMaxRequestsPtr,
+		maxConnectionsPerIpPtr, bandwidthBpsLimitPerConnectionPtr,
+		bandwidthLimitOnlyAfterBytesPtr, responseCodeOnMaxConnectionsPtr,
+		clearableFields, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.UpdateMappingSecurityRule(
+		liaison.mappingQueryRepo, liaison.mappingCmdRepo,
+		liaison.activityRecordCmdRepo, updateDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "MappingSecurityRuleUpdated")
+}
+
+func (liaison *VirtualHostLiaison) DeleteMappingSecurityRule(
+	untrustedInput map[string]any,
+) LiaisonOutput {
+	requiredParams := []string{"id"}
+	err := liaisonHelper.RequiredParamsInspector(untrustedInput, requiredParams)
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	ruleId, err := valueObject.NewMappingSecurityRuleId(untrustedInput["id"])
+	if err != nil {
+		return NewLiaisonOutput(UserError, err.Error())
+	}
+
+	operatorAccountId := LocalOperatorAccountId
+	if untrustedInput["operatorAccountId"] != nil {
+		operatorAccountId, err = valueObject.NewAccountId(untrustedInput["operatorAccountId"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	operatorIpAddress := LocalOperatorIpAddress
+	if untrustedInput["operatorIpAddress"] != nil {
+		operatorIpAddress, err = valueObject.NewIpAddress(untrustedInput["operatorIpAddress"])
+		if err != nil {
+			return NewLiaisonOutput(UserError, err.Error())
+		}
+	}
+
+	deleteDto := dto.NewDeleteMappingSecurityRule(
+		ruleId, operatorAccountId, operatorIpAddress,
+	)
+
+	err = useCase.DeleteMappingSecurityRule(
+		liaison.mappingQueryRepo, liaison.mappingCmdRepo,
+		liaison.activityRecordCmdRepo, deleteDto,
+	)
+	if err != nil {
+		return NewLiaisonOutput(InfraError, err.Error())
+	}
+
+	return NewLiaisonOutput(Success, "MappingSecurityRuleDeleted")
+}

+ 0 - 415
src/presentation/service/account.go

@@ -1,415 +0,0 @@
-package service
-
-import (
-	"errors"
-
-	"github.com/goinfinite/os/src/domain/dto"
-	"github.com/goinfinite/os/src/domain/useCase"
-	"github.com/goinfinite/os/src/domain/valueObject"
-	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
-	accountInfra "github.com/goinfinite/os/src/infra/account"
-	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
-	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
-)
-
-var LocalOperatorAccountId, _ = valueObject.NewAccountId(0)
-var LocalOperatorIpAddress = valueObject.IpAddressSystem
-
-type AccountService struct {
-	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
-	accountQueryRepo      *accountInfra.AccountQueryRepo
-	accountCmdRepo        *accountInfra.AccountCmdRepo
-	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
-}
-
-func NewAccountService(
-	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
-	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *AccountService {
-	return &AccountService{
-		persistentDbSvc:       persistentDbSvc,
-		accountQueryRepo:      accountInfra.NewAccountQueryRepo(persistentDbSvc),
-		accountCmdRepo:        accountInfra.NewAccountCmdRepo(persistentDbSvc),
-		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
-	}
-}
-
-func (service *AccountService) Read(input map[string]interface{}) ServiceOutput {
-	if input["id"] != nil {
-		input["accountId"] = input["id"]
-	}
-
-	var idPtr *valueObject.AccountId
-	if input["id"] != nil {
-		id, err := valueObject.NewAccountId(input["id"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		idPtr = &id
-	}
-
-	var usernamePtr *valueObject.Username
-	if input["name"] != nil {
-		username, err := valueObject.NewUsername(input["username"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		usernamePtr = &username
-	}
-
-	shouldIncludeSecureAccessPublicKeys := false
-	if input["shouldIncludeSecureAccessPublicKeys"] != nil {
-		var err error
-		shouldIncludeSecureAccessPublicKeys, err = voHelper.InterfaceToBool(
-			input["shouldIncludeSecureAccessPublicKeys"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-	}
-
-	paginationDto := useCase.MarketplaceDefaultPagination
-	if input["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(input["pageNumber"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidPageNumber"))
-		}
-		paginationDto.PageNumber = pageNumber
-	}
-
-	if input["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(input["itemsPerPage"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidItemsPerPage"))
-		}
-		paginationDto.ItemsPerPage = itemsPerPage
-	}
-
-	if input["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(input["sortBy"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortBy = &sortBy
-	}
-
-	if input["sortDirection"] != nil {
-		sortDirection, err := valueObject.NewPaginationSortDirection(
-			input["sortDirection"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortDirection = &sortDirection
-	}
-
-	if input["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(input["lastSeenId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.LastSeenId = &lastSeenId
-	}
-
-	readRequestDto := dto.ReadAccountsRequest{
-		Pagination:                          paginationDto,
-		AccountId:                           idPtr,
-		AccountUsername:                     usernamePtr,
-		ShouldIncludeSecureAccessPublicKeys: &shouldIncludeSecureAccessPublicKeys,
-	}
-
-	accountsList, err := useCase.ReadAccounts(service.accountQueryRepo, readRequestDto)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, accountsList)
-}
-
-func (service *AccountService) Create(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"username", "password"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	username, err := valueObject.NewUsername(input["username"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	password, err := valueObject.NewPassword(input["password"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	isSuperAdmin := false
-	if input["isSuperAdmin"] != nil {
-		isSuperAdmin, err = voHelper.InterfaceToBool(input["isSuperAdmin"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateAccount(
-		username, password, isSuperAdmin, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.CreateAccount(
-		service.accountQueryRepo, service.accountCmdRepo,
-		service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "AccountCreated")
-}
-
-func (service *AccountService) Update(input map[string]interface{}) ServiceOutput {
-	if input["id"] != nil {
-		input["accountId"] = input["id"]
-	}
-
-	requiredParams := []string{"accountId"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	accountId, err := valueObject.NewAccountId(input["accountId"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var passwordPtr *valueObject.Password
-	if input["password"] != nil && input["password"] != "" {
-		password, err := valueObject.NewPassword(input["password"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		passwordPtr = &password
-	}
-
-	var isSuperAdminPtr *bool
-	if input["isSuperAdmin"] != nil {
-		isSuperAdmin, err := voHelper.InterfaceToBool(input["isSuperAdmin"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		isSuperAdminPtr = &isSuperAdmin
-	}
-
-	var shouldUpdateApiKeyPtr *bool
-	if input["shouldUpdateApiKey"] != nil {
-		shouldUpdateApiKey, err := voHelper.InterfaceToBool(input["shouldUpdateApiKey"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		shouldUpdateApiKeyPtr = &shouldUpdateApiKey
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	updateDto := dto.NewUpdateAccount(
-		accountId, passwordPtr, isSuperAdminPtr, shouldUpdateApiKeyPtr,
-		operatorAccountId, operatorIpAddress,
-	)
-
-	if updateDto.ShouldUpdateApiKey != nil && *updateDto.ShouldUpdateApiKey {
-		newKey, err := useCase.UpdateAccountApiKey(
-			service.accountQueryRepo, service.accountCmdRepo,
-			service.activityRecordCmdRepo, updateDto,
-		)
-		if err != nil {
-			return NewServiceOutput(InfraError, err.Error())
-		}
-		return NewServiceOutput(Success, newKey)
-	}
-
-	err = useCase.UpdateAccount(
-		service.accountQueryRepo, service.accountCmdRepo,
-		service.activityRecordCmdRepo, updateDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "AccountUpdated")
-}
-
-func (service *AccountService) Delete(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"accountId"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	accountId, err := valueObject.NewAccountId(input["accountId"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteAccount(accountId, operatorAccountId, operatorIpAddress)
-
-	err = useCase.DeleteAccount(
-		service.accountQueryRepo, service.accountCmdRepo,
-		service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "AccountDeleted")
-}
-
-func (service *AccountService) CreateSecureAccessPublicKey(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"accountId", "content"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	keyContent, err := valueObject.NewSecureAccessPublicKeyContent(input["content"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	keyName, err := valueObject.NewSecureAccessPublicKeyName(input["name"])
-	if err != nil {
-		keyName, err = keyContent.ReadOnlyKeyName()
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	accountId, err := valueObject.NewAccountId(input["accountId"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateSecureAccessPublicKey(
-		accountId, keyContent, keyName, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.CreateSecureAccessPublicKey(
-		service.accountCmdRepo, service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "SecureAccessPublicKeyCreated")
-}
-
-func (service *AccountService) DeleteSecureAccessPublicKey(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"secureAccessPublicKeyId"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	keyId, err := valueObject.NewSecureAccessPublicKeyId(
-		input["secureAccessPublicKeyId"],
-	)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteSecureAccessPublicKey(
-		keyId, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.DeleteSecureAccessPublicKey(
-		service.accountQueryRepo, service.accountCmdRepo,
-		service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "SecureAccessPublicKeyDeleted")
-}

+ 0 - 284
src/presentation/service/cron.go

@@ -1,284 +0,0 @@
-package service
-
-import (
-	"errors"
-
-	"github.com/goinfinite/os/src/domain/dto"
-	"github.com/goinfinite/os/src/domain/useCase"
-	"github.com/goinfinite/os/src/domain/valueObject"
-	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
-	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
-	cronInfra "github.com/goinfinite/os/src/infra/cron"
-	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
-)
-
-type CronService struct {
-	cronQueryRepo         *cronInfra.CronQueryRepo
-	cronCmdRepo           *cronInfra.CronCmdRepo
-	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
-}
-
-func NewCronService(
-	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *CronService {
-	return &CronService{
-		cronQueryRepo:         cronInfra.NewCronQueryRepo(),
-		cronCmdRepo:           cronInfra.NewCronCmdRepo(),
-		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
-	}
-}
-
-func (service *CronService) Read(input map[string]interface{}) ServiceOutput {
-	var idPtr *valueObject.CronId
-	if input["id"] != nil {
-		id, err := valueObject.NewCronId(input["id"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		idPtr = &id
-	}
-
-	var commentPtr *valueObject.CronComment
-	if input["comment"] != nil {
-		slug, err := valueObject.NewCronComment(input["comment"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		commentPtr = &slug
-	}
-
-	paginationDto := useCase.MarketplaceDefaultPagination
-	if input["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(input["pageNumber"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidPageNumber"))
-		}
-		paginationDto.PageNumber = pageNumber
-	}
-
-	if input["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(input["itemsPerPage"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidItemsPerPage"))
-		}
-		paginationDto.ItemsPerPage = itemsPerPage
-	}
-
-	if input["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(input["sortBy"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortBy = &sortBy
-	}
-
-	if input["sortDirection"] != nil {
-		sortDirection, err := valueObject.NewPaginationSortDirection(
-			input["sortDirection"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortDirection = &sortDirection
-	}
-
-	if input["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(input["lastSeenId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.LastSeenId = &lastSeenId
-	}
-
-	readDto := dto.ReadCronsRequest{
-		Pagination:  paginationDto,
-		CronId:      idPtr,
-		CronComment: commentPtr,
-	}
-
-	cronsList, err := useCase.ReadCrons(service.cronQueryRepo, readDto)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, cronsList)
-}
-
-func (service *CronService) Create(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"schedule", "command"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	schedule, err := valueObject.NewCronSchedule(input["schedule"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	command, err := valueObject.NewUnixCommand(input["command"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var commentPtr *valueObject.CronComment
-	if input["comment"] != nil && input["comment"] != "" {
-		comment, err := valueObject.NewCronComment(input["comment"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		commentPtr = &comment
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateCron(
-		schedule, command, commentPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.CreateCron(
-		service.cronCmdRepo, service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "CronCreated")
-}
-
-func (service *CronService) Update(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"id"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	id, err := valueObject.NewCronId(input["id"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var schedulePtr *valueObject.CronSchedule
-	if input["schedule"] != nil {
-		schedule, err := valueObject.NewCronSchedule(input["schedule"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		schedulePtr = &schedule
-	}
-
-	var commandPtr *valueObject.UnixCommand
-	if input["command"] != nil {
-		command, err := valueObject.NewUnixCommand(input["command"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		commandPtr = &command
-	}
-
-	var commentPtr *valueObject.CronComment
-	if input["comment"] != nil {
-		comment, err := valueObject.NewCronComment(input["comment"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		commentPtr = &comment
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	updateDto := dto.NewUpdateCron(
-		id, schedulePtr, commandPtr, commentPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.UpdateCron(
-		service.cronQueryRepo, service.cronCmdRepo, service.activityRecordCmdRepo,
-		updateDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "CronUpdated")
-}
-
-func (service *CronService) Delete(input map[string]interface{}) ServiceOutput {
-	var idPtr *valueObject.CronId
-	if input["cronId"] != nil {
-		id, err := valueObject.NewCronId(input["cronId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		idPtr = &id
-	}
-
-	var commentPtr *valueObject.CronComment
-	if input["comment"] != nil {
-		comment, err := valueObject.NewCronComment(input["comment"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		commentPtr = &comment
-	}
-
-	var err error
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteCron(
-		idPtr, commentPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.DeleteCron(
-		service.cronQueryRepo, service.cronCmdRepo, service.activityRecordCmdRepo,
-		deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "CronDeleted")
-}

+ 0 - 352
src/presentation/service/database.go

@@ -1,352 +0,0 @@
-package service
-
-import (
-	"github.com/goinfinite/os/src/domain/dto"
-	"github.com/goinfinite/os/src/domain/useCase"
-	"github.com/goinfinite/os/src/domain/valueObject"
-	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
-	databaseInfra "github.com/goinfinite/os/src/infra/database"
-	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
-	sharedHelper "github.com/goinfinite/os/src/presentation/shared/helper"
-)
-
-type DatabaseService struct {
-	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
-	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
-	availabilityInspector *sharedHelper.ServiceAvailabilityInspector
-}
-
-func NewDatabaseService(
-	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
-	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *DatabaseService {
-	return &DatabaseService{
-		persistentDbSvc:       persistentDbSvc,
-		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
-		availabilityInspector: sharedHelper.NewServiceAvailabilityInspector(
-			persistentDbSvc,
-		),
-	}
-}
-
-func (service *DatabaseService) Read(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"dbType"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbType, err := valueObject.NewDatabaseType(input["dbType"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	serviceName, err := valueObject.NewServiceName(dbType.String())
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-	if !service.availabilityInspector.IsAvailable(serviceName) {
-		return NewServiceOutput(InfraError, sharedHelper.ServiceUnavailableError)
-	}
-
-	requestPagination, err := serviceHelper.PaginationParser(
-		input, useCase.DatabasesDefaultPagination,
-	)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var databaseNamePtr *valueObject.DatabaseName
-	if input["name"] != nil {
-		databaseName, err := valueObject.NewDatabaseName(input["name"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		databaseNamePtr = &databaseName
-	}
-
-	var usernamePtr *valueObject.DatabaseUsername
-	if input["username"] != nil {
-		username, err := valueObject.NewDatabaseUsername(input["username"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		usernamePtr = &username
-	}
-
-	requestDto := dto.ReadDatabasesRequest{
-		Pagination:   requestPagination,
-		DatabaseName: databaseNamePtr,
-		DatabaseType: &dbType,
-		Username:     usernamePtr,
-	}
-
-	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
-
-	responseDto, err := useCase.ReadDatabases(databaseQueryRepo, requestDto)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, responseDto)
-}
-
-func (service *DatabaseService) Create(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"dbType", "dbName"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbType, err := valueObject.NewDatabaseType(input["dbType"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbName, err := valueObject.NewDatabaseName(input["dbName"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	serviceName, err := valueObject.NewServiceName(dbType.String())
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-	if !service.availabilityInspector.IsAvailable(serviceName) {
-		return NewServiceOutput(InfraError, sharedHelper.ServiceUnavailableError)
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateDatabase(dbName, operatorAccountId, operatorIpAddress)
-
-	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
-	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
-
-	err = useCase.CreateDatabase(
-		databaseQueryRepo, databaseCmdRepo, service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "DatabaseCreated")
-}
-
-func (service *DatabaseService) Delete(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"dbType", "dbName"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbType, err := valueObject.NewDatabaseType(input["dbType"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	serviceName, err := valueObject.NewServiceName(dbType.String())
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-	if !service.availabilityInspector.IsAvailable(serviceName) {
-		return NewServiceOutput(InfraError, sharedHelper.ServiceUnavailableError)
-	}
-
-	dbName, err := valueObject.NewDatabaseName(input["dbName"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteDatabase(dbName, operatorAccountId, operatorIpAddress)
-
-	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
-	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
-
-	err = useCase.DeleteDatabase(
-		databaseQueryRepo, databaseCmdRepo, service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "DatabaseDeleted")
-}
-
-func (service *DatabaseService) CreateUser(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"dbType", "dbName", "username", "password"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbType, err := valueObject.NewDatabaseType(input["dbType"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	serviceName, err := valueObject.NewServiceName(dbType.String())
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-	if !service.availabilityInspector.IsAvailable(serviceName) {
-		return NewServiceOutput(InfraError, sharedHelper.ServiceUnavailableError)
-	}
-
-	dbName, err := valueObject.NewDatabaseName(input["dbName"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbUsername, err := valueObject.NewDatabaseUsername(input["username"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbPassword, err := valueObject.NewPassword(input["password"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbPrivileges := []valueObject.DatabasePrivilege{
-		valueObject.DatabasePrivilege("ALL"),
-	}
-	if input["privileges"] != nil {
-		var assertOk bool
-		dbPrivileges, assertOk = input["privileges"].([]valueObject.DatabasePrivilege)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidDatabasePrivileges")
-		}
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateDatabaseUser(
-		dbName, dbUsername, dbPassword, dbPrivileges, operatorAccountId,
-		operatorIpAddress,
-	)
-
-	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
-	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
-
-	err = useCase.CreateDatabaseUser(
-		databaseQueryRepo, databaseCmdRepo, service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "DatabaseUserCreated")
-}
-
-func (service *DatabaseService) DeleteUser(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"dbType", "dbName", "dbUser"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbType, err := valueObject.NewDatabaseType(input["dbType"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	serviceName, err := valueObject.NewServiceName(dbType.String())
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-	if !service.availabilityInspector.IsAvailable(serviceName) {
-		return NewServiceOutput(InfraError, sharedHelper.ServiceUnavailableError)
-	}
-
-	dbName, err := valueObject.NewDatabaseName(input["dbName"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	dbUsername, err := valueObject.NewDatabaseUsername(input["dbUser"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteDatabaseUser(
-		dbName, dbUsername, operatorAccountId, operatorIpAddress,
-	)
-
-	databaseQueryRepo := databaseInfra.NewDatabaseQueryRepo(dbType)
-	databaseCmdRepo := databaseInfra.NewDatabaseCmdRepo(dbType)
-
-	err = useCase.DeleteDatabaseUser(
-		databaseQueryRepo, databaseCmdRepo, service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "DatabaseUserDeleted")
-}

+ 0 - 134
src/presentation/service/runtime.go

@@ -1,134 +0,0 @@
-package service
-
-import (
-	"github.com/goinfinite/os/src/domain/dto"
-	"github.com/goinfinite/os/src/domain/entity"
-	"github.com/goinfinite/os/src/domain/useCase"
-	"github.com/goinfinite/os/src/domain/valueObject"
-	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
-	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	runtimeInfra "github.com/goinfinite/os/src/infra/runtime"
-	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
-	sharedHelper "github.com/goinfinite/os/src/presentation/shared/helper"
-)
-
-type RuntimeService struct {
-	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
-	availabilityInspector *sharedHelper.ServiceAvailabilityInspector
-	runtimeQueryRepo      runtimeInfra.RuntimeQueryRepo
-	runtimeCmdRepo        *runtimeInfra.RuntimeCmdRepo
-	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
-}
-
-func NewRuntimeService(
-	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
-	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *RuntimeService {
-	return &RuntimeService{
-		persistentDbSvc: persistentDbSvc,
-		availabilityInspector: sharedHelper.NewServiceAvailabilityInspector(
-			persistentDbSvc,
-		),
-		runtimeQueryRepo:      runtimeInfra.RuntimeQueryRepo{},
-		runtimeCmdRepo:        runtimeInfra.NewRuntimeCmdRepo(persistentDbSvc),
-		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
-	}
-}
-
-func (service *RuntimeService) ReadPhpConfigs(
-	input map[string]interface{},
-) ServiceOutput {
-	serviceName, _ := valueObject.NewServiceName("php-webserver")
-	if !service.availabilityInspector.IsAvailable(serviceName) {
-		return NewServiceOutput(InfraError, sharedHelper.ServiceUnavailableError)
-	}
-
-	hostname, err := valueObject.NewFqdn(input["hostname"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	phpConfigs, err := useCase.ReadPhpConfigs(service.runtimeQueryRepo, hostname)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, phpConfigs)
-}
-
-func (service *RuntimeService) UpdatePhpConfigs(
-	input map[string]interface{},
-) ServiceOutput {
-	serviceName, _ := valueObject.NewServiceName("php-webserver")
-	if !service.availabilityInspector.IsAvailable(serviceName) {
-		return NewServiceOutput(InfraError, sharedHelper.ServiceUnavailableError)
-	}
-
-	requiredParams := []string{"hostname", "version"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	hostname, err := valueObject.NewFqdn(input["hostname"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	phpVersion, err := valueObject.NewPhpVersion(input["version"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	phpModules := []entity.PhpModule{}
-	if _, exists := input["modules"]; exists {
-		var assertOk bool
-		phpModules, assertOk = input["modules"].([]entity.PhpModule)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidPhpModules")
-		}
-	}
-
-	phpSettings := []entity.PhpSetting{}
-	if _, exists := input["settings"]; exists {
-		var assertOk bool
-		phpSettings, assertOk = input["settings"].([]entity.PhpSetting)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidPhpSettings")
-		}
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	updateDto := dto.NewUpdatePhpConfigs(
-		hostname, phpVersion, phpModules, phpSettings, operatorAccountId,
-		operatorIpAddress,
-	)
-
-	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(service.persistentDbSvc)
-
-	err = useCase.UpdatePhpConfigs(
-		service.runtimeQueryRepo, service.runtimeCmdRepo, vhostQueryRepo,
-		service.activityRecordCmdRepo, updateDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "PhpConfigsUpdated")
-}

+ 0 - 937
src/presentation/service/services.go

@@ -1,937 +0,0 @@
-package service
-
-import (
-	"errors"
-	"log/slog"
-	"strconv"
-	"strings"
-
-	"github.com/goinfinite/os/src/domain/dto"
-	"github.com/goinfinite/os/src/domain/useCase"
-	"github.com/goinfinite/os/src/domain/valueObject"
-	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
-	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
-	infraEnvs "github.com/goinfinite/os/src/infra/envs"
-	infraHelper "github.com/goinfinite/os/src/infra/helper"
-	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	scheduledTaskInfra "github.com/goinfinite/os/src/infra/scheduledTask"
-	servicesInfra "github.com/goinfinite/os/src/infra/services"
-	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
-)
-
-type ServicesService struct {
-	persistentDbService   *internalDbInfra.PersistentDatabaseService
-	servicesQueryRepo     *servicesInfra.ServicesQueryRepo
-	servicesCmdRepo       *servicesInfra.ServicesCmdRepo
-	mappingQueryRepo      *vhostInfra.MappingQueryRepo
-	mappingCmdRepo        *vhostInfra.MappingCmdRepo
-	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
-}
-
-func NewServicesService(
-	persistentDbService *internalDbInfra.PersistentDatabaseService,
-	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *ServicesService {
-	return &ServicesService{
-		persistentDbService:   persistentDbService,
-		servicesQueryRepo:     servicesInfra.NewServicesQueryRepo(persistentDbService),
-		servicesCmdRepo:       servicesInfra.NewServicesCmdRepo(persistentDbService),
-		mappingQueryRepo:      vhostInfra.NewMappingQueryRepo(persistentDbService),
-		mappingCmdRepo:        vhostInfra.NewMappingCmdRepo(persistentDbService),
-		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
-	}
-}
-
-func (service *ServicesService) ReadInstalledItems(
-	input map[string]interface{},
-) ServiceOutput {
-	var namePtr *valueObject.ServiceName
-	if input["name"] != nil {
-		name, err := valueObject.NewServiceName(input["name"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		namePtr = &name
-	}
-
-	var naturePtr *valueObject.ServiceNature
-	if input["nature"] != nil {
-		nature, err := valueObject.NewServiceNature(input["nature"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		naturePtr = &nature
-	}
-
-	var statusPtr *valueObject.ServiceStatus
-	if input["status"] != nil {
-		status, err := valueObject.NewServiceStatus(input["status"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		statusPtr = &status
-	}
-
-	var typePtr *valueObject.ServiceType
-	if input["type"] != nil {
-		itemType, err := valueObject.NewServiceType(input["type"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		typePtr = &itemType
-	}
-
-	shouldIncludeMetrics := false
-	if input["shouldIncludeMetrics"] != nil {
-		var err error
-		shouldIncludeMetrics, err = voHelper.InterfaceToBool(input["shouldIncludeMetrics"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-	}
-
-	paginationDto := useCase.ServicesDefaultPagination
-	if input["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(input["pageNumber"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidPageNumber"))
-		}
-		paginationDto.PageNumber = pageNumber
-	}
-
-	if input["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(input["itemsPerPage"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidItemsPerPage"))
-		}
-		paginationDto.ItemsPerPage = itemsPerPage
-	}
-
-	if input["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(input["sortBy"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortBy = &sortBy
-	}
-
-	if input["sortDirection"] != nil {
-		sortDirection, err := valueObject.NewPaginationSortDirection(
-			input["sortDirection"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortDirection = &sortDirection
-	}
-
-	if input["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(input["lastSeenId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.LastSeenId = &lastSeenId
-	}
-
-	readDto := dto.ReadInstalledServicesItemsRequest{
-		Pagination:           paginationDto,
-		ServiceName:          namePtr,
-		ServiceNature:        naturePtr,
-		ServiceType:          typePtr,
-		ServiceStatus:        statusPtr,
-		ShouldIncludeMetrics: &shouldIncludeMetrics,
-	}
-
-	servicesList, err := useCase.ReadInstalledServices(
-		service.servicesQueryRepo, readDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, servicesList)
-}
-
-func (service *ServicesService) ReadInstallableItems(
-	input map[string]interface{},
-) ServiceOutput {
-	var namePtr *valueObject.ServiceName
-	if input["name"] != nil {
-		name, err := valueObject.NewServiceName(input["name"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		namePtr = &name
-	}
-
-	var naturePtr *valueObject.ServiceNature
-	if input["nature"] != nil {
-		nature, err := valueObject.NewServiceNature(input["nature"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		naturePtr = &nature
-	}
-
-	var typePtr *valueObject.ServiceType
-	if input["type"] != nil {
-		itemType, err := valueObject.NewServiceType(input["type"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		typePtr = &itemType
-	}
-
-	paginationDto := useCase.ServicesDefaultPagination
-	if input["pageNumber"] != nil {
-		pageNumber, err := voHelper.InterfaceToUint32(input["pageNumber"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidPageNumber"))
-		}
-		paginationDto.PageNumber = pageNumber
-	}
-
-	if input["itemsPerPage"] != nil {
-		itemsPerPage, err := voHelper.InterfaceToUint16(input["itemsPerPage"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidItemsPerPage"))
-		}
-		paginationDto.ItemsPerPage = itemsPerPage
-	}
-
-	if input["sortBy"] != nil {
-		sortBy, err := valueObject.NewPaginationSortBy(input["sortBy"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortBy = &sortBy
-	}
-
-	if input["sortDirection"] != nil {
-		sortDirection, err := valueObject.NewPaginationSortDirection(
-			input["sortDirection"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.SortDirection = &sortDirection
-	}
-
-	if input["lastSeenId"] != nil {
-		lastSeenId, err := valueObject.NewPaginationLastSeenId(input["lastSeenId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err)
-		}
-		paginationDto.LastSeenId = &lastSeenId
-	}
-
-	readDto := dto.ReadInstallableServicesItemsRequest{
-		Pagination:    paginationDto,
-		ServiceName:   namePtr,
-		ServiceNature: naturePtr,
-		ServiceType:   typePtr,
-	}
-
-	servicesList, err := useCase.ReadInstallableServices(
-		service.servicesQueryRepo, readDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, servicesList)
-}
-
-func (service *ServicesService) CreateInstallable(
-	input map[string]interface{},
-	shouldSchedule bool,
-) ServiceOutput {
-	requiredParams := []string{"name"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	name, err := valueObject.NewServiceName(input["name"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var versionPtr *valueObject.ServiceVersion
-	if input["version"] != nil {
-		version, err := valueObject.NewServiceVersion(input["version"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		versionPtr = &version
-	}
-
-	var startupFilePtr *valueObject.UnixFilePath
-	if input["startupFile"] != nil {
-		startupFile, err := valueObject.NewUnixFilePath(input["startupFile"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		startupFilePtr = &startupFile
-	}
-
-	var workingDirPtr *valueObject.UnixFilePath
-	if input["workingDir"] != nil {
-		workingDir, err := valueObject.NewUnixFilePath(input["workingDir"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		workingDirPtr = &workingDir
-	}
-
-	envs := []valueObject.ServiceEnv{}
-	if input["envs"] != nil {
-		var assertOk bool
-		envs, assertOk = input["envs"].([]valueObject.ServiceEnv)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidServiceEnvs")
-		}
-	}
-
-	portBindings := []valueObject.PortBinding{}
-	if input["portBindings"] != nil {
-		var assertOk bool
-		portBindings, assertOk = input["portBindings"].([]valueObject.PortBinding)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidPortBindings")
-		}
-	}
-
-	var autoStartPtr *bool
-	if input["autoStart"] != nil {
-		autoStart, err := voHelper.InterfaceToBool(input["autoStart"])
-		if err != nil {
-			return NewServiceOutput(UserError, "AutoStartMustBeBool")
-		}
-		autoStartPtr = &autoStart
-	}
-
-	var timeoutStartSecsPtr *uint
-	if input["timeoutStartSecs"] != nil {
-		timeoutStartSecs, err := voHelper.InterfaceToUint(input["timeoutStartSecs"])
-		if err != nil {
-			return NewServiceOutput(UserError, "TimeoutStartSecsMustBeUint")
-		}
-		timeoutStartSecsPtr = &timeoutStartSecs
-	}
-
-	var autoRestartPtr *bool
-	if input["autoRestart"] != nil {
-		autoRestart, err := voHelper.InterfaceToBool(
-			input["autoRestart"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, "AutoRestartMustBeBool")
-		}
-		autoRestartPtr = &autoRestart
-	}
-
-	var maxStartRetriesPtr *uint
-	if input["maxStartRetries"] != nil {
-		maxStartRetries, err := voHelper.InterfaceToUint(input["maxStartRetries"])
-		if err != nil {
-			return NewServiceOutput(UserError, "MaxStartRetriesMustBeUint")
-		}
-		maxStartRetriesPtr = &maxStartRetries
-	}
-
-	autoCreateMapping := true
-	if input["autoCreateMapping"] != nil {
-		autoCreateMapping, err = voHelper.InterfaceToBool(input["autoCreateMapping"])
-		if err != nil {
-			return NewServiceOutput(UserError, "AutoCreateMappingMustBeBool")
-		}
-	}
-
-	var mappingHostnamePtr *valueObject.Fqdn
-	if input["mappingHostname"] != nil {
-		mappingHostname, err := valueObject.NewFqdn(input["mappingHostname"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		mappingHostnamePtr = &mappingHostname
-	}
-
-	var mappingPathPtr *valueObject.MappingPath
-	if input["mappingPath"] != nil {
-		mappingPath, err := valueObject.NewMappingPath(input["mappingPath"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		mappingPathPtr = &mappingPath
-	}
-
-	var mappingUpgradeInsecureRequestsPtr *bool
-	if input["mappingUpgradeInsecureRequests"] != nil {
-		mappingUpgradeInsecureRequests, err := voHelper.InterfaceToBool(
-			input["mappingUpgradeInsecureRequests"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidMappingUpgradeInsecureRequests")
-		}
-		mappingUpgradeInsecureRequestsPtr = &mappingUpgradeInsecureRequests
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	if shouldSchedule {
-		cliCmd := infraEnvs.InfiniteOsBinary + " services create-installable"
-		installParams := []string{
-			"--name", name.String(),
-			"--auto-create-mapping", strconv.FormatBool(autoCreateMapping),
-		}
-
-		if len(envs) > 0 {
-			for _, env := range envs {
-				escapedField := infraHelper.ShellEscape{}.Quote(env.String())
-				installParams = append(installParams, "--envs", escapedField)
-			}
-		}
-
-		if len(portBindings) > 0 {
-			for _, portBinding := range portBindings {
-				escapedField := infraHelper.ShellEscape{}.Quote(portBinding.String())
-				installParams = append(installParams, "--port-bindings", escapedField)
-			}
-		}
-
-		if versionPtr != nil {
-			installParams = append(installParams, "--version", versionPtr.String())
-		}
-
-		if startupFilePtr != nil {
-			installParams = append(installParams, "--startup-file", startupFilePtr.String())
-		}
-
-		if workingDirPtr != nil {
-			installParams = append(installParams, "--working-dir", workingDirPtr.String())
-		}
-
-		if autoStartPtr != nil {
-			autoStartStr := strconv.FormatBool(*autoStartPtr)
-			installParams = append(installParams, "--auto-start", autoStartStr)
-		}
-
-		if timeoutStartSecsPtr != nil {
-			timeoutStartSecsStr := strconv.FormatUint(uint64(*timeoutStartSecsPtr), 10)
-			installParams = append(installParams, "--timeout-start-secs", timeoutStartSecsStr)
-		}
-
-		if autoRestartPtr != nil {
-			autoRestartStr := strconv.FormatBool(*autoRestartPtr)
-			installParams = append(installParams, "--auto-restart", autoRestartStr)
-		}
-
-		if maxStartRetriesPtr != nil {
-			maxStartRetriesStr := strconv.FormatUint(uint64(*maxStartRetriesPtr), 10)
-			installParams = append(installParams, "--max-start-retries", maxStartRetriesStr)
-		}
-
-		if mappingHostnamePtr != nil {
-			installParams = append(installParams, "--mapping-hostname", mappingHostnamePtr.String())
-		}
-
-		if mappingPathPtr != nil {
-			installParams = append(installParams, "--mapping-path", mappingPathPtr.String())
-		}
-
-		if mappingUpgradeInsecureRequestsPtr != nil {
-			installParams = append(
-				installParams,
-				"--mapping-upgrade-insecure-requests",
-				strconv.FormatBool(*mappingUpgradeInsecureRequestsPtr),
-			)
-		}
-
-		cliCmd += " " + strings.Join(installParams, " ")
-
-		scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(service.persistentDbService)
-		taskName, _ := valueObject.NewScheduledTaskName("CreateInstallableService")
-		taskCmd, _ := valueObject.NewUnixCommand(cliCmd)
-		taskTag, _ := valueObject.NewScheduledTaskTag("services")
-		taskTags := []valueObject.ScheduledTaskTag{taskTag}
-		timeoutSecs := uint16(1800)
-
-		scheduledTaskCreateDto := dto.NewCreateScheduledTask(
-			taskName, taskCmd, taskTags, &timeoutSecs, nil,
-		)
-
-		err = useCase.CreateScheduledTask(scheduledTaskCmdRepo, scheduledTaskCreateDto)
-		if err != nil {
-			return NewServiceOutput(InfraError, err.Error())
-		}
-
-		return NewServiceOutput(Created, "CreateInstallableServiceScheduled")
-	}
-
-	createDto := dto.NewCreateInstallableService(
-		name, envs, portBindings, versionPtr, startupFilePtr, workingDirPtr,
-		autoStartPtr, timeoutStartSecsPtr, autoRestartPtr, maxStartRetriesPtr,
-		&autoCreateMapping, mappingHostnamePtr, mappingPathPtr,
-		mappingUpgradeInsecureRequestsPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(service.persistentDbService)
-
-	err = useCase.CreateInstallableService(
-		service.servicesQueryRepo, service.servicesCmdRepo, vhostQueryRepo,
-		service.mappingCmdRepo, service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "InstallableServiceCreated")
-}
-
-func (service *ServicesService) CreateCustom(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"name", "type", "startCmd"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	name, err := valueObject.NewServiceName(input["name"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	svcType, err := valueObject.NewServiceType(input["type"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	startCmd, err := valueObject.NewUnixCommand(input["startCmd"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var versionPtr *valueObject.ServiceVersion
-	if input["version"] != nil {
-		version, err := valueObject.NewServiceVersion(input["version"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		versionPtr = &version
-	}
-
-	var execUserPtr *valueObject.UnixUsername
-	if input["execUser"] != nil {
-		execUser, err := valueObject.NewUnixUsername(input["execUser"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		execUserPtr = &execUser
-	}
-
-	envs := []valueObject.ServiceEnv{}
-	if input["envs"] != nil {
-		var assertOk bool
-		envs, assertOk = input["envs"].([]valueObject.ServiceEnv)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidServiceEnvs")
-		}
-	}
-
-	portBindings := []valueObject.PortBinding{}
-	if input["portBindings"] != nil {
-		var assertOk bool
-		portBindings, assertOk = input["portBindings"].([]valueObject.PortBinding)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidPortBindings")
-		}
-	}
-
-	var autoStartPtr *bool
-	if input["autoStart"] != nil {
-		autoStart, err := voHelper.InterfaceToBool(input["autoStart"])
-		if err != nil {
-			return NewServiceOutput(UserError, "AutoStartMustBeBool")
-		}
-		autoStartPtr = &autoStart
-	}
-
-	var timeoutStartSecsPtr *uint
-	if input["timeoutStartSecs"] != nil {
-		timeoutStartSecs, err := voHelper.InterfaceToUint(input["timeoutStartSecs"])
-		if err != nil {
-			return NewServiceOutput(UserError, "TimeoutStartSecsMustBeUint")
-		}
-		timeoutStartSecsPtr = &timeoutStartSecs
-	}
-
-	var autoRestartPtr *bool
-	if input["autoRestart"] != nil {
-		autoRestart, err := voHelper.InterfaceToBool(
-			input["autoRestart"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, "AutoRestartMustBeBool")
-		}
-		autoRestartPtr = &autoRestart
-	}
-
-	var maxStartRetriesPtr *uint
-	if input["maxStartRetries"] != nil {
-		maxStartRetries, err := voHelper.InterfaceToUint(input["maxStartRetries"])
-		if err != nil {
-			return NewServiceOutput(UserError, "MaxStartRetriesMustBeUint")
-		}
-		maxStartRetriesPtr = &maxStartRetries
-	}
-
-	var logOutputPathPtr *valueObject.UnixFilePath
-	if input["logOutputPath"] != nil {
-		logOutputPath, err := valueObject.NewUnixFilePath(input["logOutputPath"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		logOutputPathPtr = &logOutputPath
-	}
-
-	var logErrorPathPtr *valueObject.UnixFilePath
-	if input["logErrorPath"] != nil {
-		logErrorPath, err := valueObject.NewUnixFilePath(input["logErrorPath"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		logErrorPathPtr = &logErrorPath
-	}
-
-	var avatarUrlPtr *valueObject.Url
-	if input["avatarUrl"] != nil {
-		avatarUrl, err := valueObject.NewUrl(input["avatarUrl"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		avatarUrlPtr = &avatarUrl
-	}
-
-	autoCreateMapping := true
-	if input["autoCreateMapping"] != nil {
-		autoCreateMapping, err = voHelper.InterfaceToBool(input["autoCreateMapping"])
-		if err != nil {
-			return NewServiceOutput(UserError, "AutoCreateMappingMustBeBool")
-		}
-	}
-
-	var mappingHostnamePtr *valueObject.Fqdn
-	if input["mappingHostname"] != nil {
-		mappingHostname, err := valueObject.NewFqdn(input["mappingHostname"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		mappingHostnamePtr = &mappingHostname
-	}
-
-	var mappingPathPtr *valueObject.MappingPath
-	if input["mappingPath"] != nil {
-		mappingPath, err := valueObject.NewMappingPath(input["mappingPath"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		mappingPathPtr = &mappingPath
-	}
-
-	var mappingUpgradeInsecureRequestsPtr *bool
-	if input["mappingUpgradeInsecureRequests"] != nil {
-		mappingUpgradeInsecureRequests, err := voHelper.InterfaceToBool(
-			input["mappingUpgradeInsecureRequests"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidMappingUpgradeInsecureRequests")
-		}
-		mappingUpgradeInsecureRequestsPtr = &mappingUpgradeInsecureRequests
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createCustomDto := dto.NewCreateCustomService(
-		name, svcType, startCmd, envs, portBindings, nil, nil, nil, nil, nil,
-		versionPtr, execUserPtr, nil, autoStartPtr, autoRestartPtr,
-		timeoutStartSecsPtr, maxStartRetriesPtr, logOutputPathPtr, logErrorPathPtr,
-		avatarUrlPtr, &autoCreateMapping, mappingHostnamePtr, mappingPathPtr,
-		mappingUpgradeInsecureRequestsPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(service.persistentDbService)
-
-	err = useCase.CreateCustomService(
-		service.servicesQueryRepo, service.servicesCmdRepo, vhostQueryRepo,
-		service.mappingCmdRepo, service.activityRecordCmdRepo, createCustomDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "CustomServiceCreated")
-}
-
-func (service *ServicesService) Update(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"name"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	name, err := valueObject.NewServiceName(input["name"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var typePtr *valueObject.ServiceType
-	if input["type"] != nil {
-		svcType, err := valueObject.NewServiceType(input["type"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		typePtr = &svcType
-	}
-
-	var startCmdPtr *valueObject.UnixCommand
-	if input["startCmd"] != nil {
-		startCmd, err := valueObject.NewUnixCommand(input["startCmd"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		startCmdPtr = &startCmd
-	}
-
-	var statusPtr *valueObject.ServiceStatus
-	if input["status"] != nil {
-		status, err := valueObject.NewServiceStatus(input["status"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		statusPtr = &status
-	}
-
-	var versionPtr *valueObject.ServiceVersion
-	if input["version"] != nil {
-		version, err := valueObject.NewServiceVersion(input["version"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		versionPtr = &version
-	}
-
-	envs := []valueObject.ServiceEnv{}
-	if input["envs"] != nil {
-		rawEnvs, assertOk := input["envs"].([]string)
-		if !assertOk {
-			return NewServiceOutput(UserError, "EnvsMustBeStringArray")
-		}
-
-		for _, rawEnv := range rawEnvs {
-			env, err := valueObject.NewServiceEnv(rawEnv)
-			if err != nil {
-				slog.Debug(err.Error(), slog.String("env", rawEnv))
-				continue
-			}
-			envs = append(envs, env)
-		}
-	}
-
-	portBindings := []valueObject.PortBinding{}
-	if _, exists := input["portBindings"]; exists {
-		rawPortBindings, assertOk := input["portBindings"].([]string)
-		if !assertOk {
-			return NewServiceOutput(UserError, "PortBindingsMustBeStringArray")
-		}
-
-		for _, rawPortBinding := range rawPortBindings {
-			if len(rawPortBinding) == 0 {
-				continue
-			}
-
-			portBinding, err := valueObject.NewPortBinding(rawPortBinding)
-			if err != nil {
-				slog.Debug(err.Error(), slog.String("portBinding", rawPortBinding))
-				continue
-			}
-			portBindings = append(portBindings, portBinding)
-		}
-	}
-
-	var startupFilePtr *valueObject.UnixFilePath
-	if input["startupFile"] != nil {
-		startupFile, err := valueObject.NewUnixFilePath(input["startupFile"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		startupFilePtr = &startupFile
-	}
-
-	var autoStartPtr *bool
-	if input["autoStart"] != nil {
-		autoStart, err := voHelper.InterfaceToBool(input["autoStart"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		autoStartPtr = &autoStart
-	}
-
-	var autoRestartPtr *bool
-	if input["autoRestart"] != nil {
-		autoRestart, err := voHelper.InterfaceToBool(input["autoRestart"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		autoRestartPtr = &autoRestart
-	}
-
-	var timeoutStartSecsPtr *uint
-	if input["timeoutStartSecs"] != nil {
-		timeoutStartSecs, err := voHelper.InterfaceToUint(input["timeoutStartSecs"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		timeoutStartSecsPtr = &timeoutStartSecs
-	}
-
-	var maxStartRetriesPtr *uint
-	if input["maxStartRetries"] != nil {
-		maxStartRetries, err := voHelper.InterfaceToUint(input["maxStartRetries"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		maxStartRetriesPtr = &maxStartRetries
-	}
-
-	var logOutputPathPtr *valueObject.UnixFilePath
-	if input["logOutputPath"] != nil {
-		logOutputPath, err := valueObject.NewUnixFilePath(input["logOutputPath"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		logOutputPathPtr = &logOutputPath
-	}
-
-	var logErrorPathPtr *valueObject.UnixFilePath
-	if input["logErrorPath"] != nil {
-		logErrorPath, err := valueObject.NewUnixFilePath(input["logErrorPath"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		logErrorPathPtr = &logErrorPath
-	}
-
-	var avatarUrlPtr *valueObject.Url
-	if input["avatarUrl"] != nil {
-		avatarUrl, err := valueObject.NewUrl(input["avatarUrl"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		avatarUrlPtr = &avatarUrl
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	updateDto := dto.NewUpdateService(
-		name, typePtr, versionPtr, statusPtr, startCmdPtr, envs, portBindings, nil,
-		nil, nil, nil, nil, nil, nil, startupFilePtr, autoStartPtr, autoRestartPtr,
-		timeoutStartSecsPtr, maxStartRetriesPtr, logOutputPathPtr, logErrorPathPtr,
-		avatarUrlPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.UpdateService(
-		service.servicesQueryRepo, service.servicesCmdRepo, service.mappingQueryRepo,
-		service.mappingCmdRepo, service.activityRecordCmdRepo, updateDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "ServiceUpdated")
-}
-
-func (service *ServicesService) Delete(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"name"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	name, err := valueObject.NewServiceName(input["name"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteService(name, operatorAccountId, operatorIpAddress)
-
-	err = useCase.DeleteService(
-		service.servicesQueryRepo, service.servicesCmdRepo, service.mappingQueryRepo,
-		service.mappingCmdRepo, service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "ServiceDeleted")
-}

+ 0 - 313
src/presentation/service/ssl.go

@@ -1,313 +0,0 @@
-package service
-
-import (
-	"errors"
-	"strings"
-
-	"github.com/goinfinite/os/src/domain/dto"
-	"github.com/goinfinite/os/src/domain/entity"
-	"github.com/goinfinite/os/src/domain/useCase"
-	"github.com/goinfinite/os/src/domain/valueObject"
-	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
-	infraEnvs "github.com/goinfinite/os/src/infra/envs"
-	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	scheduledTaskInfra "github.com/goinfinite/os/src/infra/scheduledTask"
-	sslInfra "github.com/goinfinite/os/src/infra/ssl"
-	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
-)
-
-type SslService struct {
-	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
-	sslQueryRepo          *sslInfra.SslQueryRepo
-	sslCmdRepo            *sslInfra.SslCmdRepo
-	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
-}
-
-func NewSslService(
-	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
-	transientDbSvc *internalDbInfra.TransientDatabaseService,
-	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *SslService {
-	return &SslService{
-		persistentDbSvc:       persistentDbSvc,
-		sslQueryRepo:          sslInfra.NewSslQueryRepo(),
-		sslCmdRepo:            sslInfra.NewSslCmdRepo(persistentDbSvc, transientDbSvc),
-		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
-	}
-}
-
-func (service *SslService) SslPairReadRequestFactory(
-	serviceInput map[string]interface{},
-	withMappings bool,
-) (readRequestDto dto.ReadSslPairsRequest, err error) {
-	if serviceInput["sslPairId"] == nil && serviceInput["id"] != nil {
-		serviceInput["sslPairId"] = serviceInput["id"]
-	}
-
-	var sslPairIdPtr *valueObject.SslPairId
-	if serviceInput["sslPairId"] != nil {
-		sslPairId, err := valueObject.NewSslPairId(serviceInput["sslPairId"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		sslPairIdPtr = &sslPairId
-	}
-
-	if serviceInput["virtualHostHostname"] == nil && serviceInput["hostname"] != nil {
-		serviceInput["virtualHostHostname"] = serviceInput["hostname"]
-	}
-
-	var vhostHostnamePtr *valueObject.Fqdn
-	if serviceInput["virtualHostHostname"] != nil {
-		vhostHostname, err := valueObject.NewFqdn(serviceInput["virtualHostHostname"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		vhostHostnamePtr = &vhostHostname
-	}
-
-	altNames := []valueObject.SslHostname{}
-	if serviceInput["altNames"] != nil {
-		var assertOk bool
-		altNames, assertOk = serviceInput["altNames"].([]valueObject.SslHostname)
-		if !assertOk {
-			return readRequestDto, errors.New("InvalidAltNamesStructure")
-		}
-	}
-
-	timeParamNames := []string{
-		"issuedBeforeAt", "issuedAfterAt", "expiresBeforeAt", "expiresAfterAt",
-	}
-	timeParamPtrs := serviceHelper.TimeParamsParser(timeParamNames, serviceInput)
-
-	requestPagination, err := serviceHelper.PaginationParser(
-		serviceInput, useCase.SslPairsDefaultPagination,
-	)
-	if err != nil {
-		return readRequestDto, err
-	}
-
-	return dto.ReadSslPairsRequest{
-		Pagination:          requestPagination,
-		SslPairId:           sslPairIdPtr,
-		VirtualHostHostname: vhostHostnamePtr,
-		AltNames:            altNames,
-		IssuedBeforeAt:      timeParamPtrs["issuedBeforeAt"],
-		IssuedAfterAt:       timeParamPtrs["issuedAfterAt"],
-		ExpiresBeforeAt:     timeParamPtrs["expiresBeforeAt"],
-		ExpiresAfterAt:      timeParamPtrs["expiresAfterAt"],
-	}, nil
-}
-
-func (service *SslService) Read(
-	serviceInput map[string]interface{},
-) ServiceOutput {
-	readRequestDto, err := service.SslPairReadRequestFactory(serviceInput, false)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	readResponseDto, err := useCase.ReadSslPairs(service.sslQueryRepo, readRequestDto)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, readResponseDto)
-}
-
-func (service *SslService) Create(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"virtualHostsHostnames", "certificate", "key"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	vhostHostnames, assertOk := input["virtualHostsHostnames"].([]valueObject.Fqdn)
-	if !assertOk {
-		return NewServiceOutput(UserError, errors.New("InvalidVirtualHostsStructure"))
-	}
-
-	certContent, err := valueObject.NewSslCertificateContent(input["certificate"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-	certEntity, err := entity.NewSslCertificate(certContent)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var chainCertsPtr *entity.SslCertificate
-	if input["chainCertificates"] != nil {
-		chainCertContent, err := valueObject.NewSslCertificateContent(input["chainCertificates"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("SslCertificateChainContentError"))
-		}
-		chainCertEntity, err := entity.NewSslCertificate(chainCertContent)
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("SslCertificateChainParseError"))
-		}
-		chainCertsPtr = &chainCertEntity
-	}
-
-	privateKeyContent, err := valueObject.NewSslPrivateKey(input["key"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateSslPair(
-		vhostHostnames, certEntity, chainCertsPtr, privateKeyContent,
-		operatorAccountId, operatorIpAddress,
-	)
-
-	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(service.persistentDbSvc)
-
-	err = useCase.CreateSslPair(
-		vhostQueryRepo, service.sslCmdRepo, service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "SslPairCreated")
-}
-
-func (service *SslService) CreatePubliclyTrusted(
-	input map[string]interface{},
-	shouldSchedule bool,
-) ServiceOutput {
-	if input["hostname"] != nil && input["virtualHostHostname"] == nil {
-		input["virtualHostHostname"] = input["hostname"]
-	}
-
-	if input["vhostHostname"] != nil && input["virtualHostHostname"] == nil {
-		input["virtualHostHostname"] = input["vhostHostname"]
-	}
-
-	requiredParams := []string{"virtualHostHostname"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	vhostHostname, err := valueObject.NewFqdn(input["virtualHostHostname"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	if shouldSchedule {
-		cliCmd := infraEnvs.InfiniteOsBinary + " ssl create-trusted"
-		installParams := []string{
-			"--hostname", vhostHostname.String(),
-		}
-		cliCmd += " " + strings.Join(installParams, " ")
-
-		scheduledTaskCmdRepo := scheduledTaskInfra.NewScheduledTaskCmdRepo(service.persistentDbSvc)
-		taskName, _ := valueObject.NewScheduledTaskName("CreatePubliclyTrustedSslPair")
-		taskCmd, _ := valueObject.NewUnixCommand(cliCmd)
-		taskTag, _ := valueObject.NewScheduledTaskTag("ssl")
-		taskTags := []valueObject.ScheduledTaskTag{taskTag}
-		timeoutSecs := uint16(1800)
-
-		scheduledTaskCreateDto := dto.NewCreateScheduledTask(
-			taskName, taskCmd, taskTags, &timeoutSecs, nil,
-		)
-
-		err = useCase.CreateScheduledTask(scheduledTaskCmdRepo, scheduledTaskCreateDto)
-		if err != nil {
-			return NewServiceOutput(InfraError, err.Error())
-		}
-
-		return NewServiceOutput(Created, "PubliclyTrustedSslPairCreationScheduled")
-	}
-
-	createDto := dto.NewCreatePubliclyTrustedSslPair(
-		vhostHostname, operatorAccountId, operatorIpAddress,
-	)
-
-	vhostQueryRepo := vhostInfra.NewVirtualHostQueryRepo(service.persistentDbSvc)
-
-	_, err = useCase.CreatePubliclyTrustedSslPair(
-		vhostQueryRepo, service.sslCmdRepo, service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "PubliclyTrustedSslPairCreated")
-}
-
-func (service *SslService) Delete(input map[string]interface{}) ServiceOutput {
-	if input["id"] == nil && input["sslPairId"] != nil {
-		input["id"] = input["sslPairId"]
-	}
-
-	requiredParams := []string{"id"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	pairId, err := valueObject.NewSslPairId(input["id"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	err = useCase.DeleteSslPair(
-		service.sslQueryRepo, service.sslCmdRepo, service.activityRecordCmdRepo,
-		dto.NewDeleteSslPair(pairId, operatorAccountId, operatorIpAddress),
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "SslPairDeleted")
-}

+ 0 - 994
src/presentation/service/virtualHost.go

@@ -1,994 +0,0 @@
-package service
-
-import (
-	"errors"
-
-	"github.com/goinfinite/os/src/domain/dto"
-	"github.com/goinfinite/os/src/domain/useCase"
-	"github.com/goinfinite/os/src/domain/valueObject"
-	voHelper "github.com/goinfinite/os/src/domain/valueObject/helper"
-	activityRecordInfra "github.com/goinfinite/os/src/infra/activityRecord"
-	internalDbInfra "github.com/goinfinite/os/src/infra/internalDatabase"
-	servicesInfra "github.com/goinfinite/os/src/infra/services"
-	vhostInfra "github.com/goinfinite/os/src/infra/vhost"
-	serviceHelper "github.com/goinfinite/os/src/presentation/service/helper"
-
-	tkValueObject "github.com/goinfinite/tk/src/domain/valueObject"
-	tkVoUtil "github.com/goinfinite/tk/src/domain/valueObject/util"
-)
-
-type VirtualHostService struct {
-	persistentDbSvc       *internalDbInfra.PersistentDatabaseService
-	trailDbSvc            *internalDbInfra.TrailDatabaseService
-	vhostQueryRepo        *vhostInfra.VirtualHostQueryRepo
-	vhostCmdRepo          *vhostInfra.VirtualHostCmdRepo
-	mappingQueryRepo      *vhostInfra.MappingQueryRepo
-	mappingCmdRepo        *vhostInfra.MappingCmdRepo
-	activityRecordCmdRepo *activityRecordInfra.ActivityRecordCmdRepo
-}
-
-func NewVirtualHostService(
-	persistentDbSvc *internalDbInfra.PersistentDatabaseService,
-	trailDbSvc *internalDbInfra.TrailDatabaseService,
-) *VirtualHostService {
-	return &VirtualHostService{
-		persistentDbSvc:       persistentDbSvc,
-		trailDbSvc:            trailDbSvc,
-		vhostQueryRepo:        vhostInfra.NewVirtualHostQueryRepo(persistentDbSvc),
-		vhostCmdRepo:          vhostInfra.NewVirtualHostCmdRepo(persistentDbSvc),
-		mappingQueryRepo:      vhostInfra.NewMappingQueryRepo(persistentDbSvc),
-		mappingCmdRepo:        vhostInfra.NewMappingCmdRepo(persistentDbSvc),
-		activityRecordCmdRepo: activityRecordInfra.NewActivityRecordCmdRepo(trailDbSvc),
-	}
-}
-
-func (service *VirtualHostService) VirtualHostReadRequestFactory(
-	serviceInput map[string]interface{},
-	withMappings bool,
-) (readRequestDto dto.ReadVirtualHostsRequest, err error) {
-	var hostnamePtr *valueObject.Fqdn
-	if serviceInput["hostname"] != nil {
-		hostname, err := valueObject.NewFqdn(serviceInput["hostname"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		hostnamePtr = &hostname
-	}
-
-	var typePtr *valueObject.VirtualHostType
-	if serviceInput["type"] != nil {
-		vhostType, err := valueObject.NewVirtualHostType(serviceInput["type"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		typePtr = &vhostType
-	}
-
-	var rootDirectoryPtr *valueObject.UnixFilePath
-	if serviceInput["rootDirectory"] != nil {
-		rootDirectory, err := valueObject.NewUnixFilePath(serviceInput["rootDirectory"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		rootDirectoryPtr = &rootDirectory
-	}
-
-	var parentHostnamePtr *valueObject.Fqdn
-	if serviceInput["parentHostname"] != nil {
-		parentHostname, err := valueObject.NewFqdn(serviceInput["parentHostname"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		parentHostnamePtr = &parentHostname
-	}
-
-	if serviceInput["withMappings"] != nil {
-		withMappings, err = voHelper.InterfaceToBool(serviceInput["withMappings"])
-		if err != nil {
-			return readRequestDto, err
-		}
-	}
-
-	timeParamNames := []string{"createdBeforeAt", "createdAfterAt"}
-	timeParamPtrs := serviceHelper.TimeParamsParser(timeParamNames, serviceInput)
-
-	requestPagination, err := serviceHelper.PaginationParser(
-		serviceInput, useCase.VirtualHostsDefaultPagination,
-	)
-	if err != nil {
-		return readRequestDto, err
-	}
-
-	return dto.ReadVirtualHostsRequest{
-		Pagination:      requestPagination,
-		Hostname:        hostnamePtr,
-		VirtualHostType: typePtr,
-		RootDirectory:   rootDirectoryPtr,
-		ParentHostname:  parentHostnamePtr,
-		WithMappings:    &withMappings,
-		CreatedBeforeAt: timeParamPtrs["createdBeforeAt"],
-		CreatedAfterAt:  timeParamPtrs["createdAfterAt"],
-	}, nil
-}
-
-func (service *VirtualHostService) Read(
-	serviceInput map[string]interface{},
-) ServiceOutput {
-	readRequestDto, err := service.VirtualHostReadRequestFactory(serviceInput, false)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	readResponseDto, err := useCase.ReadVirtualHosts(service.vhostQueryRepo, readRequestDto)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, readResponseDto)
-}
-
-func (service *VirtualHostService) Create(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"hostname"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	hostname, err := valueObject.NewFqdn(input["hostname"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	vhostType := valueObject.VirtualHostTypeTopLevel
-	if input["type"] != nil {
-		vhostType, err = valueObject.NewVirtualHostType(input["type"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	isWildcard := false
-	if input["isWildcard"] != nil {
-		isWildcard, err = voHelper.InterfaceToBool(input["isWildcard"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	var parentHostnamePtr *valueObject.Fqdn
-	if input["parentHostname"] != nil {
-		parentHostname, err := valueObject.NewFqdn(input["parentHostname"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		parentHostnamePtr = &parentHostname
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateVirtualHost(
-		hostname, vhostType, &isWildcard, parentHostnamePtr,
-		operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.CreateVirtualHost(
-		service.vhostQueryRepo, service.vhostCmdRepo, service.activityRecordCmdRepo,
-		createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "VirtualHostCreated")
-}
-
-func (service *VirtualHostService) Update(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"hostname"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	hostname, err := valueObject.NewFqdn(input["hostname"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var isWildcardPtr *bool
-	if input["isWildcard"] != nil {
-		isWildcard, err := voHelper.InterfaceToBool(input["isWildcard"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidIsWildcard"))
-		}
-		isWildcardPtr = &isWildcard
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	updateDto := dto.NewUpdateVirtualHost(
-		hostname, isWildcardPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.UpdateVirtualHost(
-		service.vhostQueryRepo, service.vhostCmdRepo, service.activityRecordCmdRepo,
-		updateDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "VirtualHostUpdated")
-}
-
-func (service *VirtualHostService) Delete(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"hostname"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	hostname, err := valueObject.NewFqdn(input["hostname"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteVirtualHost(hostname, operatorAccountId, operatorIpAddress)
-	err = useCase.DeleteVirtualHost(
-		service.vhostQueryRepo, service.vhostCmdRepo,
-		service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "VirtualHostDeleted")
-}
-
-func (service *VirtualHostService) ReadWithMappings(
-	serviceInput map[string]interface{},
-) ServiceOutput {
-	readRequestDto, err := service.VirtualHostReadRequestFactory(serviceInput, true)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	readResponseDto, err := useCase.ReadVirtualHosts(service.vhostQueryRepo, readRequestDto)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, readResponseDto)
-}
-
-func (service *VirtualHostService) CreateMapping(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"hostname", "path", "targetType"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	hostname, err := valueObject.NewFqdn(input["hostname"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	path, err := valueObject.NewMappingPath(input["path"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	matchPattern := valueObject.MappingMatchPatternBeginsWith
-	if input["matchPattern"] != nil {
-		matchPattern, err = valueObject.NewMappingMatchPattern(input["matchPattern"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	targetType, err := valueObject.NewMappingTargetType(input["targetType"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var targetValuePtr *valueObject.MappingTargetValue
-	if input["targetValue"] != nil {
-		targetValue, err := valueObject.NewMappingTargetValue(
-			input["targetValue"], targetType,
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		targetValuePtr = &targetValue
-	}
-
-	var targetHttpResponseCodePtr *valueObject.HttpResponseCode
-	if input["targetHttpResponseCode"] != nil {
-		if input["targetHttpResponseCode"] == "" {
-			input["targetHttpResponseCode"] = 301
-		}
-		targetHttpResponseCode, err := valueObject.NewHttpResponseCode(
-			input["targetHttpResponseCode"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		targetHttpResponseCodePtr = &targetHttpResponseCode
-	}
-
-	var shouldUpgradeInsecureRequestsPtr *bool
-	if input["shouldUpgradeInsecureRequests"] != nil {
-		shouldUpgradeInsecureRequests, err := tkVoUtil.InterfaceToBool(
-			input["shouldUpgradeInsecureRequests"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidShouldUpgradeInsecureRequests")
-		}
-		shouldUpgradeInsecureRequestsPtr = &shouldUpgradeInsecureRequests
-	}
-
-	var mappingSecurityRuleIdPtr *valueObject.MappingSecurityRuleId
-	if input["mappingSecurityRuleId"] != nil && input["mappingSecurityRuleId"] != "" {
-		mappingSecurityRuleId, err := valueObject.NewMappingSecurityRuleId(
-			input["mappingSecurityRuleId"],
-		)
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		mappingSecurityRuleIdPtr = &mappingSecurityRuleId
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateMapping(
-		hostname, path, matchPattern, targetType, targetValuePtr,
-		targetHttpResponseCodePtr, shouldUpgradeInsecureRequestsPtr,
-		mappingSecurityRuleIdPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	servicesQueryRepo := servicesInfra.NewServicesQueryRepo(service.persistentDbSvc)
-
-	err = useCase.CreateMapping(
-		service.vhostQueryRepo, service.mappingCmdRepo, servicesQueryRepo,
-		service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "MappingCreated")
-}
-
-func (service *VirtualHostService) DeleteMapping(
-	input map[string]interface{},
-) ServiceOutput {
-	if input["mappingId"] == nil && input["id"] != nil {
-		input["mappingId"] = input["id"]
-	}
-
-	requiredParams := []string{"mappingId"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	id, err := valueObject.NewMappingId(input["mappingId"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteMapping(id, operatorAccountId, operatorIpAddress)
-	err = useCase.DeleteMapping(
-		service.mappingQueryRepo, service.mappingCmdRepo,
-		service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, "MappingDeleted")
-}
-
-func (service *VirtualHostService) UpdateMapping(input map[string]interface{}) ServiceOutput {
-	requiredParams := []string{"id"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	id, err := valueObject.NewMappingId(input["id"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var pathPtr *valueObject.MappingPath
-	if input["path"] != nil {
-		path, err := valueObject.NewMappingPath(input["path"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		pathPtr = &path
-	}
-
-	var matchPatternPtr *valueObject.MappingMatchPattern
-	if input["matchPattern"] != nil {
-		matchPattern, err := valueObject.NewMappingMatchPattern(input["matchPattern"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		matchPatternPtr = &matchPattern
-	}
-
-	var targetTypePtr *valueObject.MappingTargetType
-	if input["targetType"] != nil {
-		targetType, err := valueObject.NewMappingTargetType(input["targetType"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		targetTypePtr = &targetType
-	}
-
-	var targetValuePtr *valueObject.MappingTargetValue
-	if input["targetValue"] != nil {
-		if targetTypePtr == nil {
-			mappingEntity, err := service.mappingQueryRepo.ReadFirst(
-				dto.ReadMappingsRequest{MappingId: &id},
-			)
-			if err != nil {
-				return NewServiceOutput(InfraError, "ReadMappingEntityToRetrieveTargetTypeError")
-			}
-			targetTypePtr = &mappingEntity.TargetType
-		}
-
-		targetValue, err := valueObject.NewMappingTargetValue(input["targetValue"], *targetTypePtr)
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		targetValuePtr = &targetValue
-	}
-
-	var targetHttpResponseCodePtr *valueObject.HttpResponseCode
-	if input["targetHttpResponseCode"] != nil {
-		targetHttpResponseCode, err := valueObject.NewHttpResponseCode(input["targetHttpResponseCode"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		targetHttpResponseCodePtr = &targetHttpResponseCode
-	}
-
-	var shouldUpgradeInsecureRequestsPtr *bool
-	if input["shouldUpgradeInsecureRequests"] != nil {
-		shouldUpgradeInsecureRequests, err := tkVoUtil.InterfaceToBool(input["shouldUpgradeInsecureRequests"])
-		if err != nil {
-			return NewServiceOutput(UserError, errors.New("InvalidShouldUpgradeInsecureRequests"))
-		}
-		shouldUpgradeInsecureRequestsPtr = &shouldUpgradeInsecureRequests
-	}
-
-	var mappingSecurityRuleIdPtr *valueObject.MappingSecurityRuleId
-	if input["mappingSecurityRuleId"] != nil && input["mappingSecurityRuleId"] != "" {
-		mappingSecurityRuleId, err := valueObject.NewMappingSecurityRuleId(input["mappingSecurityRuleId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		mappingSecurityRuleIdPtr = &mappingSecurityRuleId
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	updateDto := dto.NewUpdateMapping(
-		id, pathPtr, matchPatternPtr, targetTypePtr, targetValuePtr,
-		targetHttpResponseCodePtr, shouldUpgradeInsecureRequestsPtr,
-		mappingSecurityRuleIdPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.UpdateMapping(
-		service.mappingQueryRepo, service.mappingCmdRepo,
-		service.activityRecordCmdRepo, updateDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "MappingUpdated")
-}
-
-func (service *VirtualHostService) MappingSecurityRuleReadRequestFactory(
-	serviceInput map[string]interface{},
-) (readRequestDto dto.ReadMappingSecurityRulesRequest, err error) {
-	var mappingSecurityRuleIdPtr *valueObject.MappingSecurityRuleId
-	if serviceInput["id"] != nil {
-		id, err := valueObject.NewMappingSecurityRuleId(serviceInput["id"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		mappingSecurityRuleIdPtr = &id
-	}
-
-	var mappingSecurityRuleNamePtr *valueObject.MappingSecurityRuleName
-	if serviceInput["name"] != nil {
-		name, err := valueObject.NewMappingSecurityRuleName(serviceInput["name"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		mappingSecurityRuleNamePtr = &name
-	}
-
-	var allowedIpPtr *tkValueObject.CidrBlock
-	if serviceInput["allowedIp"] != nil {
-		allowedIp, err := tkValueObject.NewCidrBlock(serviceInput["allowedIp"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		allowedIpPtr = &allowedIp
-	}
-
-	var blockedIpPtr *tkValueObject.CidrBlock
-	if serviceInput["blockedIp"] != nil {
-		blockedIp, err := tkValueObject.NewCidrBlock(serviceInput["blockedIp"])
-		if err != nil {
-			return readRequestDto, err
-		}
-		blockedIpPtr = &blockedIp
-	}
-
-	timeParamNames := []string{"createdBeforeAt", "createdAfterAt"}
-	timeParamPtrs := serviceHelper.TimeParamsParser(timeParamNames, serviceInput)
-
-	requestPagination, err := serviceHelper.PaginationParser(
-		serviceInput, useCase.MappingSecurityRulesDefaultPagination,
-	)
-	if err != nil {
-		return readRequestDto, err
-	}
-
-	return dto.ReadMappingSecurityRulesRequest{
-		Pagination:              requestPagination,
-		MappingSecurityRuleId:   mappingSecurityRuleIdPtr,
-		MappingSecurityRuleName: mappingSecurityRuleNamePtr,
-		AllowedIp:               allowedIpPtr,
-		BlockedIp:               blockedIpPtr,
-		CreatedBeforeAt:         timeParamPtrs["createdBeforeAt"],
-		CreatedAfterAt:          timeParamPtrs["createdAfterAt"],
-	}, nil
-}
-
-func (service *VirtualHostService) ReadMappingSecurityRules(
-	serviceInput map[string]interface{},
-) ServiceOutput {
-	readRequestDto, err := service.MappingSecurityRuleReadRequestFactory(serviceInput)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	readResponseDto, err := useCase.ReadMappingSecurityRules(
-		service.mappingQueryRepo, readRequestDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, readResponseDto)
-}
-
-func (service *VirtualHostService) CreateMappingSecurityRule(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"name"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	name, err := valueObject.NewMappingSecurityRuleName(input["name"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var descriptionPtr *valueObject.MappingSecurityRuleDescription
-	if input["description"] != nil {
-		description, err := valueObject.NewMappingSecurityRuleDescription(input["description"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		descriptionPtr = &description
-	}
-
-	allowedIps := []tkValueObject.CidrBlock{}
-	if input["allowedIps"] != nil {
-		allowedIpsInput, assertOk := input["allowedIps"].([]tkValueObject.CidrBlock)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidAllowedIps")
-		}
-		allowedIps = allowedIpsInput
-	}
-
-	blockedIps := []tkValueObject.CidrBlock{}
-	if input["blockedIps"] != nil {
-		blockedIpsInput, assertOk := input["blockedIps"].([]tkValueObject.CidrBlock)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidBlockedIps")
-		}
-		blockedIps = blockedIpsInput
-	}
-
-	var rpsSoftLimitPerIpPtr *uint
-	if input["rpsSoftLimitPerIp"] != nil && input["rpsSoftLimitPerIp"] != "" {
-		softLimit, err := tkVoUtil.InterfaceToUint(input["rpsSoftLimitPerIp"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidRpsSoftLimitPerIp")
-		}
-		rpsSoftLimitPerIpPtr = &softLimit
-	}
-
-	var rpsHardLimitPerIpPtr *uint
-	if input["rpsHardLimitPerIp"] != nil && input["rpsHardLimitPerIp"] != "" {
-		hardLimit, err := tkVoUtil.InterfaceToUint(input["rpsHardLimitPerIp"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidRpsHardLimitPerIp")
-		}
-		rpsHardLimitPerIpPtr = &hardLimit
-	}
-
-	var responseCodeOnMaxRequestsPtr *uint
-	if input["responseCodeOnMaxRequests"] != nil && input["responseCodeOnMaxRequests"] != "" {
-		responseCode, err := tkVoUtil.InterfaceToUint(input["responseCodeOnMaxRequests"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidResponseCodeOnMaxRequests")
-		}
-		responseCodeOnMaxRequestsPtr = &responseCode
-	}
-
-	var maxConnectionsPerIpPtr *uint
-	if input["maxConnectionsPerIp"] != nil && input["maxConnectionsPerIp"] != "" {
-		maxConns, err := tkVoUtil.InterfaceToUint(input["maxConnectionsPerIp"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidMaxConnectionsPerIp")
-		}
-		maxConnectionsPerIpPtr = &maxConns
-	}
-
-	var bandwidthBpsLimitPerConnectionPtr *valueObject.Byte
-	if input["bandwidthBpsLimitPerConnection"] != nil && input["bandwidthBpsLimitPerConnection"] != "" {
-		bandwidthBpsLimit, err := valueObject.NewByte(input["bandwidthBpsLimitPerConnection"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidBandwidthBpsLimitPerConnection")
-		}
-		bandwidthBpsLimitPerConnectionPtr = &bandwidthBpsLimit
-	}
-
-	var bandwidthLimitOnlyAfterBytesPtr *valueObject.Byte
-	if input["bandwidthLimitOnlyAfterBytes"] != nil && input["bandwidthLimitOnlyAfterBytes"] != "" {
-		bandwidthLimitOnlyAfterBytes, err := valueObject.NewByte(input["bandwidthLimitOnlyAfterBytes"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidBandwidthLimitOnlyAfterBytes")
-		}
-		bandwidthLimitOnlyAfterBytesPtr = &bandwidthLimitOnlyAfterBytes
-	}
-
-	var responseCodeOnMaxConnectionsPtr *uint
-	if input["responseCodeOnMaxConnections"] != nil && input["responseCodeOnMaxConnections"] != "" {
-		responseCode, err := tkVoUtil.InterfaceToUint(input["responseCodeOnMaxConnections"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidResponseCodeOnMaxConnections")
-		}
-		responseCodeOnMaxConnectionsPtr = &responseCode
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	createDto := dto.NewCreateMappingSecurityRule(
-		name, descriptionPtr, allowedIps, blockedIps, rpsSoftLimitPerIpPtr,
-		rpsHardLimitPerIpPtr, responseCodeOnMaxRequestsPtr, maxConnectionsPerIpPtr,
-		bandwidthBpsLimitPerConnectionPtr, bandwidthLimitOnlyAfterBytesPtr,
-		responseCodeOnMaxConnectionsPtr, operatorAccountId, operatorIpAddress,
-	)
-
-	mappingSecurityRuleId, err := useCase.CreateMappingSecurityRule(
-		service.mappingQueryRepo, service.mappingCmdRepo,
-		service.activityRecordCmdRepo, createDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Created, map[string]interface{}{
-		"id": mappingSecurityRuleId.Uint64(),
-	})
-}
-
-func (service *VirtualHostService) UpdateMappingSecurityRule(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"id"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	id, err := valueObject.NewMappingSecurityRuleId(input["id"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	var namePtr *valueObject.MappingSecurityRuleName
-	if input["name"] != nil {
-		name, err := valueObject.NewMappingSecurityRuleName(input["name"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		namePtr = &name
-	}
-
-	var descriptionPtr *valueObject.MappingSecurityRuleDescription
-	if input["description"] != nil {
-		description, err := valueObject.NewMappingSecurityRuleDescription(input["description"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-		descriptionPtr = &description
-	}
-
-	allowedIps := []tkValueObject.CidrBlock{}
-	if input["allowedIps"] != nil {
-		var assertOk bool
-		allowedIps, assertOk = input["allowedIps"].([]tkValueObject.CidrBlock)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidAllowedIps")
-		}
-	}
-
-	blockedIps := []tkValueObject.CidrBlock{}
-	if input["blockedIps"] != nil {
-		var assertOk bool
-		blockedIps, assertOk = input["blockedIps"].([]tkValueObject.CidrBlock)
-		if !assertOk {
-			return NewServiceOutput(UserError, "InvalidBlockedIps")
-		}
-	}
-
-	var rpsSoftLimitPerIpPtr *uint
-	if input["rpsSoftLimitPerIp"] != nil && input["rpsSoftLimitPerIp"] != "" {
-		softLimit, err := tkVoUtil.InterfaceToUint(input["rpsSoftLimitPerIp"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidRpsSoftLimitPerIp")
-		}
-		rpsSoftLimitPerIpPtr = &softLimit
-	}
-
-	var rpsHardLimitPerIpPtr *uint
-	if input["rpsHardLimitPerIp"] != nil && input["rpsHardLimitPerIp"] != "" {
-		hardLimit, err := tkVoUtil.InterfaceToUint(input["rpsHardLimitPerIp"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidRpsHardLimitPerIp")
-		}
-		rpsHardLimitPerIpPtr = &hardLimit
-	}
-
-	var responseCodeOnMaxRequestsPtr *uint
-	if input["responseCodeOnMaxRequests"] != nil && input["responseCodeOnMaxRequests"] != "" {
-		responseCode, err := tkVoUtil.InterfaceToUint(input["responseCodeOnMaxRequests"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidResponseCodeOnMaxRequests")
-		}
-		responseCodeOnMaxRequestsPtr = &responseCode
-	}
-
-	var maxConnectionsPerIpPtr *uint
-	if input["maxConnectionsPerIp"] != nil && input["maxConnectionsPerIp"] != "" {
-		maxConns, err := tkVoUtil.InterfaceToUint(input["maxConnectionsPerIp"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidMaxConnectionsPerIp")
-		}
-		maxConnectionsPerIpPtr = &maxConns
-	}
-
-	var bandwidthBpsLimitPerConnectionPtr *valueObject.Byte
-	if input["bandwidthBpsLimitPerConnection"] != nil && input["bandwidthBpsLimitPerConnection"] != "" {
-		bandwidthBpsLimit, err := valueObject.NewByte(input["bandwidthBpsLimitPerConnection"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidBandwidthBpsLimitPerConnection")
-		}
-		bandwidthBpsLimitPerConnectionPtr = &bandwidthBpsLimit
-	}
-
-	var bandwidthLimitOnlyAfterBytesPtr *valueObject.Byte
-	if input["bandwidthLimitOnlyAfterBytes"] != nil && input["bandwidthLimitOnlyAfterBytes"] != "" {
-		bandwidthLimitOnlyAfterBytes, err := valueObject.NewByte(input["bandwidthLimitOnlyAfterBytes"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidBandwidthLimitOnlyAfterBytes")
-		}
-		bandwidthLimitOnlyAfterBytesPtr = &bandwidthLimitOnlyAfterBytes
-	}
-
-	var responseCodeOnMaxConnectionsPtr *uint
-	if input["responseCodeOnMaxConnections"] != nil && input["responseCodeOnMaxConnections"] != "" {
-		responseCode, err := tkVoUtil.InterfaceToUint(input["responseCodeOnMaxConnections"])
-		if err != nil {
-			return NewServiceOutput(UserError, "InvalidResponseCodeOnMaxConnections")
-		}
-		responseCodeOnMaxConnectionsPtr = &responseCode
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	updateDto := dto.NewUpdateMappingSecurityRule(
-		id, namePtr, descriptionPtr, allowedIps, blockedIps,
-		rpsSoftLimitPerIpPtr, rpsHardLimitPerIpPtr, responseCodeOnMaxRequestsPtr,
-		maxConnectionsPerIpPtr, bandwidthBpsLimitPerConnectionPtr,
-		bandwidthLimitOnlyAfterBytesPtr, responseCodeOnMaxConnectionsPtr,
-		operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.UpdateMappingSecurityRule(
-		service.mappingQueryRepo, service.mappingCmdRepo,
-		service.activityRecordCmdRepo, updateDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "MappingSecurityRuleUpdated")
-}
-
-func (service *VirtualHostService) DeleteMappingSecurityRule(
-	input map[string]interface{},
-) ServiceOutput {
-	requiredParams := []string{"id"}
-	err := serviceHelper.RequiredParamsInspector(input, requiredParams)
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	ruleId, err := valueObject.NewMappingSecurityRuleId(input["id"])
-	if err != nil {
-		return NewServiceOutput(UserError, err.Error())
-	}
-
-	operatorAccountId := LocalOperatorAccountId
-	if input["operatorAccountId"] != nil {
-		operatorAccountId, err = valueObject.NewAccountId(input["operatorAccountId"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	operatorIpAddress := LocalOperatorIpAddress
-	if input["operatorIpAddress"] != nil {
-		operatorIpAddress, err = valueObject.NewIpAddress(input["operatorIpAddress"])
-		if err != nil {
-			return NewServiceOutput(UserError, err.Error())
-		}
-	}
-
-	deleteDto := dto.NewDeleteMappingSecurityRule(
-		ruleId, operatorAccountId, operatorIpAddress,
-	)
-
-	err = useCase.DeleteMappingSecurityRule(
-		service.mappingQueryRepo, service.mappingCmdRepo,
-		service.activityRecordCmdRepo, deleteDto,
-	)
-	if err != nil {
-		return NewServiceOutput(InfraError, err.Error())
-	}
-
-	return NewServiceOutput(Success, "MappingSecurityRuleDeleted")
-}

+ 0 - 72
src/presentation/ui/assets/additional.js

@@ -60,63 +60,6 @@ window.__unocss = {
   },
 };
 
-async function jsonAjax(method, url, payload, shouldDisplayToast = true) {
-  const loadingOverlayElement = document.getElementById("loading-overlay");
-  loadingOverlayElement.classList.add("htmx-request");
-
-  try {
-    const requestSettings = {
-      method: method,
-      headers: {
-        Accept: "application/json",
-        "Content-Type": "application/json",
-      },
-    };
-    if (Object.keys(payload).length > 0) {
-      requestSettings.body = JSON.stringify(payload);
-    }
-    const response = await fetch(url, requestSettings);
-    const parsedResponse = await response.json();
-
-    loadingOverlayElement.classList.remove("htmx-request");
-
-    if (!response.ok) {
-      throw new Error(parsedResponse.body);
-    }
-
-    if (shouldDisplayToast) {
-      Alpine.store("toast").displayToast(parsedResponse.body, "success");
-    }
-    return parsedResponse.body;
-  } catch (error) {
-    loadingOverlayElement.classList.remove("htmx-request");
-
-    if (shouldDisplayToast) {
-      Alpine.store("toast").displayToast(error.message, "danger");
-      return;
-    }
-    throw error;
-  }
-}
-
-function createRandomPassword() {
-  const passwordLength = 16;
-  const chars =
-    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+";
-
-  let passwordContent = "";
-  let passwordIterationCount = 0;
-  while (passwordIterationCount < passwordLength) {
-    const randomIndex = Math.floor(Math.random() * chars.length);
-    const indexAfterRandomIndex = randomIndex + 1;
-    passwordContent += chars.substring(randomIndex, indexAfterRandomIndex);
-
-    passwordIterationCount++;
-  }
-
-  return passwordContent;
-}
-
 function createFilterQueryParams(filtersObject, paginationObject) {
   const queryParams = new URLSearchParams();
 
@@ -157,25 +100,10 @@ function downloadFile(nameWithExtension, content, mimeType = "text/plain") {
   document.body.removeChild(downloadFileElement);
 }
 
-function registerAlpineState(stateFunction) {
-  if (window.Alpine) {
-    stateFunction();
-    return;
-  }
-
-  document.addEventListener("alpine:init", stateFunction);
-}
-
 window.Infinite = {
   Envs: {
     AccessTokenCookieKey: "os-access-token",
   },
-  CreateRandomPassword: createRandomPassword,
   CreateFilterQueryParams: createFilterQueryParams,
   DownloadFile: downloadFile,
-  RegisterAlpineState: registerAlpineState,
 };
-
-document.addEventListener("alpine:init", () => {
-  window.Infinite.JsonAjax = jsonAjax;
-});

+ 5 - 2
src/presentation/ui/component/form/dropzone.templ

@@ -1,6 +1,9 @@
 package componentForm
 
-import _ "embed"
+import (
+	_ "embed"
+	"github.com/goinfinite/ui/src/toolset"
+)
 
 //go:embed dropzoneState.js
 var dropzoneAlpineState string
@@ -14,7 +17,7 @@ type DropzoneInputDto struct {
 
 templ Dropzone(inputDto DropzoneInputDto) {
 	<!-- Dropzone JavaScript -->
-	@templ.Raw(`<script type="text/javascript">` + dropzoneAlpineState + `</script>`)
+	@uiToolset.MinifierTemplateJs(&dropzoneAlpineState)
 	<!-- Dropzone HTML -->
 	<div
 		x-data="dropzone"

+ 2 - 4
src/presentation/ui/component/form/dropzoneState.js

@@ -1,6 +1,4 @@
-Infinite.RegisterAlpineState(dropzoneAlpineState);
-
-function dropzoneAlpineState() {
+UiToolset.RegisterAlpineState(() => {
   Alpine.data("dropzone", () => ({
     files: [],
     updateFileInput() {
@@ -29,4 +27,4 @@ function dropzoneAlpineState() {
       });
     },
   }));
-}
+});

+ 2 - 4
src/presentation/ui/component/form/fileUploadTextInputFileContentReader.templ

@@ -1,9 +1,7 @@
 package componentForm
 
 script FileUploadTextInputFileContentReaderLocalState() {
-	Infinite.RegisterAlpineState(fileUploadTextInputFileContentReaderAlpineState);
-
-	function fileUploadTextInputFileContentReaderAlpineState() {
+	UiToolset.RegisterAlpineState(() => {
 		Alpine.data('fileUploadTextInputFileContentReader', () => ({
 			uploadedFileName: '',
 			get uploadedFileNameLabel() {
@@ -32,7 +30,7 @@ script FileUploadTextInputFileContentReaderLocalState() {
 				reader.readAsText(uploadedFile);
 			}
 		}));
-	}
+	});
 }
 
 templ FileUploadTextInputFileContentReader(

+ 2 - 4
src/presentation/ui/component/form/multiColumnRepeatableFieldset.templ

@@ -8,9 +8,7 @@ type RepeatableField struct {
 }
 
 script MultiColumnRepeatableFieldsetLocalState(id string) {
-	Infinite.RegisterAlpineState(multiColumnRepeatableFieldsetAlpineState);
-
-	function multiColumnRepeatableFieldsetAlpineState() {
+	UiToolset.RegisterAlpineState(() => {
 		Alpine.data(id+'MultiColumnRepeatableFieldset', () => ({
 			newFieldset: {},
 			registeredFieldsets: [],
@@ -58,7 +56,7 @@ script MultiColumnRepeatableFieldsetLocalState(id string) {
 				});
 			},
 		}));
-	};
+	});
 }
 
 templ MultiColumnRepeatableFieldset(

+ 2 - 4
src/presentation/ui/component/form/multiSelectInputState.js

@@ -1,6 +1,4 @@
-Infinite.RegisterAlpineState(multiSelectInputAlpineState);
-
-function multiSelectInputAlpineState() {
+UiToolset.RegisterAlpineState(() => {
   Alpine.data("multiSelectInput", () => ({
     getFormattedSelectedItems(bindSelectedItemsPath) {
       if (bindSelectedItemsPath.length == 0) {
@@ -26,4 +24,4 @@ function multiSelectInputAlpineState() {
       this.shouldExpandOptions = true;
     },
   }));
-}
+});

+ 3 - 2
src/presentation/ui/component/form/passwordInput.templ

@@ -2,6 +2,7 @@ package componentForm
 
 import (
 	_ "embed"
+	"github.com/goinfinite/ui/src/toolset"
 	"strconv"
 )
 
@@ -42,7 +43,7 @@ type PasswordInputDto struct {
 
 templ PasswordInput(passwordInputDto PasswordInputDto) {
 	<!-- PasswordInput JavaScript -->
-	@templ.Raw(`<script type="text/javascript">` + passwordInputAlpineState + `</script>`)
+	@uiToolset.MinifierTemplateJs(&passwordInputAlpineState)
 	<!-- PasswordInput HTML -->
 	<div class="w-full space-y-2" x-data="passwordInput">
 		<div class="flex space-x-2">
@@ -130,7 +131,7 @@ templ PasswordInputClientSide(
 					class="from-os-300 via-os-300 absolute -top-2 left-1.5 z-[1] cursor-text bg-gradient-to-t via-50% to-transparent to-50% px-1.5 text-xs font-bold text-neutral-50 text-opacity-80 transition-all before:absolute before:left-0 before:top-0 before:z-[-1] before:block before:h-full before:w-full before:bg-white before:transition-all peer-placeholder-shown:top-2.5 peer-placeholder-shown:text-sm peer-placeholder-shown:font-normal peer-autofill:-top-2 peer-hover:text-opacity-90 peer-focus:-top-2 peer-focus:cursor-default peer-focus:text-opacity-100"
 				></label>
 			</div>
-			@generateRandomPasswordButton(bindModelPath, "Infinite.CreateRandomPassword()")
+			@generateRandomPasswordButton(bindModelPath, "UiToolset.CreateRandomPassword()")
 		</div>
 	</div>
 }

+ 4 - 6
src/presentation/ui/component/form/passwordInputState.js

@@ -1,11 +1,9 @@
-Infinite.RegisterAlpineState(passwordInputAlpineState);
-
-function passwordInputAlpineState() {
+UiToolset.RegisterAlpineState(() => {
   Alpine.data("passwordInput", () => ({
     // RandomPasswordGeneratorState
     isPasswordReadable: false,
     generateRandomPassword() {
-      const passwordContent = Infinite.CreateRandomPassword();
+      const passwordContent = UiToolset.CreateRandomPassword();
 
       this.displayPasswordStrengthCriteria = false;
       this.updatePasswordStrengthPercentage(passwordContent);
@@ -35,7 +33,7 @@ function passwordInputAlpineState() {
         passwordStrengthPercentage += 20;
       }
 
-      if (/[1-9]/.test(password)) {
+      if (/[0-9]/.test(password)) {
         this.passwordStrengthCriteria.hasNumbers = true;
         passwordStrengthPercentage += 20;
       }
@@ -58,4 +56,4 @@ function passwordInputAlpineState() {
       this.passwordStrengthPercentage = passwordStrengthPercentage;
     },
   }));
-}
+});

+ 5 - 2
src/presentation/ui/component/form/selectInput.templ

@@ -1,6 +1,9 @@
 package componentForm
 
-import _ "embed"
+import (
+	_ "embed"
+	"github.com/goinfinite/ui/src/toolset"
+)
 
 templ SelectInput(
 	id, label, bindModelPath, onSelect string,
@@ -134,7 +137,7 @@ templ MultiSelectInput(
 	options []string,
 ) {
 	<!-- MultiSelectInput JavaScript -->
-	@templ.Raw(`<script type="text/javascript">` + multiSelectInputAlpineState + `</script>`)
+	@uiToolset.MinifierTemplateJs(&multiSelectInputAlpineState)
 	<!-- MultiSelectInput HTML -->
 	<div
 		class="relative w-full"

+ 3 - 2
src/presentation/ui/component/structural/deleteModal.templ

@@ -2,7 +2,7 @@ package componentStructural
 
 templ DeleteCriticalContent(elNameStatePath, elIdStatePath string) {
 	<h3 class="text-pretty mb-3 text-xl font-bold leading-relaxed">
-		Are you sure you want to delete?
+		Are You sure You Want to Delete
 		if elNameStatePath != "" {
 			<strong
 				x-show={ elNameStatePath + " !== ''" }
@@ -13,8 +13,9 @@ templ DeleteCriticalContent(elNameStatePath, elIdStatePath string) {
 		if elIdStatePath != "" {
 			<strong x-show={ elIdStatePath + " !== ''" }>
 				#<span x-text={ elIdStatePath }></span>
-			</strong>?
+			</strong>
 		}
+		?
 	</h3>
 	<p>This process cannot be undone. This will permanently delete the element(s).</p>
 }

+ 49 - 23
src/presentation/ui/layout/login/login.templ

@@ -3,53 +3,79 @@ package uiLayout
 import (
 	_ "embed"
 	infraEnvs "github.com/goinfinite/os/src/infra/envs"
-	componentForm "github.com/goinfinite/os/src/presentation/ui/component/form"
 	layoutMain "github.com/goinfinite/os/src/presentation/ui/layout/main"
+	"github.com/goinfinite/ui/src/control"
 	"github.com/goinfinite/ui/src/display"
+	"github.com/goinfinite/ui/src/form"
+	"github.com/goinfinite/ui/src/toolset"
 )
 
+type LoginLayoutSettings struct {
+	PrefilledUsername string
+	PrefilledPassword string
+}
+
 //go:embed state.js
 var loginLayoutAlpineState string
 
-templ Login() {
+templ Login(componentSettings LoginLayoutSettings) {
 	<!DOCTYPE html>
 	<html>
 		@layoutMain.HeadTag()
 		<!-- Login Layout JavaScript -->
-		@templ.Raw(`<script type="text/javascript">` + loginLayoutAlpineState + `</script>`)
+		@uiToolset.MinifierTemplateJs(&loginLayoutAlpineState)
 		<!-- Login Layout HTML -->
 		<body class="bg-primary-500 h-vh flex flex-col items-center justify-center bg-[url('/assets/infinite-os-bg.jpg')] bg-cover bg-no-repeat text-neutral-50" un-cloak>
 			@uiDisplay.LoadingOverlay(uiDisplay.LoadingOverlaySettings{})
-			<div>
-				<a href="https://goinfinite.net/os/" target="_blank" class="mb-4 flex w-[120px] gap-2 hover:opacity-90">
+			<div class="flex flex-col gap-4">
+				<a href="https://goinfinite.net/os/" target="_blank" class="flex w-[120px] gap-2 hover:opacity-90">
 					<img src="/assets/os-logo.svg" alt="Infinite Os" class="fill-white"/>
 					<p class="text-[8px] text-neutral-500">v{ infraEnvs.InfiniteOsVersion }</p>
 				</a>
+				if componentSettings.PrefilledPassword != "" {
+					@uiDisplay.Alert(uiDisplay.AlertSettings{
+						Variation:   uiDisplay.AlertVariationWarning,
+						Title:       "Prefilled Password Being Used",
+						Description: "For security reasons, please change your password after logging in.",
+						Size:        uiDisplay.AlertSizeSm,
+						IsCloseable: false,
+					})
+				}
 				<form
 					x-data="login"
-					onsubmit="event.preventDefault()"
-					class="bg-os-400 min-w-110 flex flex-col items-center justify-center gap-4 rounded-lg p-8 drop-shadow-md"
+					class="bg-primary-300/50 min-w-110 flex flex-col items-center justify-center gap-4 rounded p-8 drop-shadow-md"
+					@submit="event.preventDefault(); createSessionToken()"
 				>
-					<div class="mb-1 w-full">
+					<div class="flex w-full flex-col gap-1">
 						<h1 class="text-2xl">Hello Again!</h1>
-						<p class="mt-1 text-sm">Please enter your credentials to continue.</p>
+						<p class="text-sm">Please enter your credentials to continue.</p>
 					</div>
-					@componentForm.InputField(componentForm.InputFieldDto{
-						Type:               "text",
-						Id:                 "username",
-						Label:              "Username",
-						BindModelValuePath: "username",
+					if componentSettings.PrefilledUsername != "" {
+						<input type="hidden" id="prefilledUsername" value={ componentSettings.PrefilledUsername }/>
+					}
+					@uiForm.InputField(uiForm.InputFieldSettings{
+						InputType:       "text",
+						InputName:       "username",
+						Label:           "Username",
+						TwoWayStatePath: "username",
+					})
+					if componentSettings.PrefilledPassword != "" {
+						<input type="hidden" id="prefilledPassword" value={ componentSettings.PrefilledPassword }/>
+					}
+					@uiForm.InputField(uiForm.InputFieldSettings{
+						InputType:       "password",
+						InputName:       "password",
+						Label:           "Password",
+						TwoWayStatePath: "password",
 					})
-					@componentForm.PasswordInput(componentForm.PasswordInputDto{
-						Id:                                "password",
-						Label:                             "Password",
-						BindModelPath:                     "password",
-						ShouldIncludeGenRandomPassBtn:     false,
-						ShouldIncludePassStrengthCriteria: false,
+					@uiControl.Button(uiControl.ButtonSettings{
+						Label:                "login",
+						IconLeft:             "ph-sign-in",
+						BackgroundColor:      "secondary-500",
+						BackgroundColorHover: "secondary-200",
+						IsSubmit:             true,
+						IsFullWidth:          true,
 					})
-					@componentForm.SubmitButton(
-						"login-btn", "Login", "ph-sign-in", "createSessionToken()", false,
-					)
 				</form>
 			</div>
 			@uiDisplay.Toast(uiDisplay.ToastSettings{})

+ 11 - 8
src/presentation/ui/layout/login/state.js

@@ -1,12 +1,10 @@
-Infinite.RegisterAlpineState(loginAlpineState);
-
-function loginAlpineState() {
+UiToolset.RegisterAlpineState(() => {
   Alpine.data("login", () => ({
     username: "",
     password: "",
     createSessionToken() {
       const shouldDisplayToast = false;
-      Infinite.JsonAjax(
+      UiToolset.JsonAjax(
         "POST",
         "/api/v1/auth/login/",
         {
@@ -17,15 +15,20 @@ function loginAlpineState() {
       )
         .then((authResponse) => {
           Alpine.store("toast").displayToast("LoginSuccessful", "success");
+
+          UiToolset.ToggleLoadingOverlay(true);
           document.cookie = `${Infinite.Envs.AccessTokenCookieKey}=${authResponse.tokenStr}; path=/`;
           window.location.href = "/overview/";
         })
-        .catch((error) =>
-          Alpine.store("toast").displayToast(error.message, "danger")
-        );
+        .catch((error) => {
+          UiToolset.ToggleLoadingOverlay(false);
+          Alpine.store("toast").displayToast(error.message, "danger");
+        });
     },
     init() {
       document.cookie = `${Infinite.Envs.AccessTokenCookieKey}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
+      this.username = document.getElementById("prefilledUsername")?.value;
+      this.password = document.getElementById("prefilledPassword")?.value;
     },
   }));
-}
+});

+ 2 - 1
src/presentation/ui/layout/main/main.templ

@@ -7,6 +7,7 @@ import (
 	layoutSidebar "github.com/goinfinite/os/src/presentation/ui/layout/sidebar"
 	"github.com/goinfinite/ui/src/display"
 	uiImport "github.com/goinfinite/ui/src/import"
+	"github.com/goinfinite/ui/src/toolset"
 	"os"
 )
 
@@ -37,7 +38,7 @@ templ Main(componentSettings MainLayoutSettings) {
 	<html>
 		@HeadTag()
 		<!-- MainLayout JavaScript -->
-		@templ.Raw(`<script type="text/javascript">` + mainLayoutAlpineState + `</script>`)
+		@uiToolset.MinifierTemplateJs(&mainLayoutAlpineState)
 		if isDevMode, _ := voHelper.InterfaceToBool(os.Getenv("DEV_MODE")); isDevMode {
 			@templ.JSFuncCall("devWsHotReload")
 		}

+ 2 - 1
src/presentation/ui/layout/setup/setup.templ

@@ -6,6 +6,7 @@ import (
 	componentForm "github.com/goinfinite/os/src/presentation/ui/component/form"
 	layoutMain "github.com/goinfinite/os/src/presentation/ui/layout/main"
 	"github.com/goinfinite/ui/src/display"
+	"github.com/goinfinite/ui/src/toolset"
 )
 
 //go:embed state.js
@@ -16,7 +17,7 @@ templ Setup() {
 	<html>
 		@layoutMain.HeadTag()
 		<!-- Setup Layout JavaScript -->
-		@templ.Raw(`<script type="text/javascript">` + setupLayoutAlpineState + `</script>`)
+		@uiToolset.MinifierTemplateJs(&setupLayoutAlpineState)
 		<!-- Setup Layout HTML -->
 		<body
 			x-data="setup"

+ 4 - 6
src/presentation/ui/layout/setup/state.js

@@ -1,12 +1,10 @@
-Infinite.RegisterAlpineState(setupAlpineState);
-
-function setupAlpineState() {
+UiToolset.RegisterAlpineState(() => {
   Alpine.data("setup", () => ({
     username: "",
     password: "",
     setupInfiniteOsAndLogin() {
       const shouldDisplayToast = false;
-      Infinite.JsonAjax(
+      UiToolset.JsonAjax(
         "POST",
         "/api/v1/setup/",
         {
@@ -16,7 +14,7 @@ function setupAlpineState() {
         shouldDisplayToast
       )
         .then(() => {
-          Infinite.JsonAjax(
+          UiToolset.JsonAjax(
             "POST",
             "/api/v1/auth/login/",
             {
@@ -42,4 +40,4 @@ function setupAlpineState() {
         );
     },
   }));
-}
+});

+ 2 - 1
src/presentation/ui/presenter/accounts/index.templ

@@ -5,6 +5,7 @@ import (
 	"github.com/goinfinite/os/src/domain/entity"
 	componentForm "github.com/goinfinite/os/src/presentation/ui/component/form"
 	componentStructural "github.com/goinfinite/os/src/presentation/ui/component/structural"
+	"github.com/goinfinite/ui/src/toolset"
 	"strconv"
 )
 
@@ -13,7 +14,7 @@ var accountsIndexAlpineState string
 
 templ AccountsIndex(accountsEntities []entity.Account) {
 	<!-- AccountsIndex JavaScript -->
-	@templ.Raw(`<script type="text/javascript">` + accountsIndexAlpineState + `</script>`)
+	@uiToolset.MinifierTemplateJs(&accountsIndexAlpineState)
 	<!-- AccountsIndex HTML -->
 	<div x-data="accounts">
 		@componentStructural.PageTitle(

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