Compare commits

..

62 Commits

Author SHA1 Message Date
57970b3d10 4.5.4 2024-12-21 22:14:45 +01:00
b4d9f40c41 fix(ts_web): Fix action type and data fields in appstate for CRUD operations 2024-12-21 22:14:45 +01:00
a219725ff6 4.5.3 2024-12-21 20:21:54 +01:00
4b993fc6b3 fix(secret-management): Refactor secret management to use distinct secret bundle and group APIs. Introduce API client classes for secret bundles and groups. 2024-12-21 20:21:54 +01:00
d453da709f 4.5.2 2024-12-20 02:13:50 +01:00
50fac41c95 fix(apiclient): Implemented IService interface in Service class and improved secret bundle documentation. 2024-12-20 02:13:50 +01:00
affce1fcd1 4.5.1 2024-12-17 19:51:11 +01:00
df67ebd27a fix(core): Updated dependencies in package.json to latest versions. 2024-12-17 19:51:10 +01:00
ef5bfd435a 4.5.0 2024-12-14 20:32:17 +01:00
db07db930c feat(services): Add service management functionalities 2024-12-14 20:32:17 +01:00
f6309f600c 4.4.0 2024-11-18 19:52:15 +01:00
7477704905 feat(api-client): Add static method getImageById for Image class in api-client 2024-11-18 19:52:15 +01:00
db89d86242 4.3.21 2024-11-18 19:43:52 +01:00
b74ce05845 fix(interfaces): Remove deprecated deployment directive and update related interfaces 2024-11-18 19:43:52 +01:00
79db68a4a2 4.3.20 2024-11-18 17:48:26 +01:00
5a3ddcf39b fix(apiclient): Ensure mandatory parameter in CloudlyApiClient constructor 2024-11-18 17:48:26 +01:00
fe6bfc0a83 4.3.19 2024-11-18 13:41:29 +01:00
36a481ecd1 fix(docker): Fix improper Docker push command preventing push to the correct registry. 2024-11-18 13:41:29 +01:00
f7b2e203ed 4.3.18 2024-11-17 07:05:41 +01:00
27c98c4e32 fix(docker_tags): Updated Docker configuration to include NPM tokens. 2024-11-17 07:05:41 +01:00
79257908d0 4.3.17 2024-11-17 06:31:22 +01:00
b5ca898eac fix(Dockerfile): Corrected docker base image tag in Dockerfile for alpine compatibility. 2024-11-17 06:31:22 +01:00
53ade28931 4.3.16 2024-11-17 01:04:26 +01:00
fff4c7642d fix(infrastructure): Correct Docker image path in Dockerfile for improved build consistency. 2024-11-17 01:04:26 +01:00
dafe6574cc 4.3.15 2024-11-17 00:00:56 +01:00
b70dad4996 fix(project setup): fixed incorrect configuration in npmextra.json 2024-11-17 00:00:55 +01:00
17b0b50fbd 4.3.14 2024-11-16 22:31:17 +01:00
91a0272ab3 fix(docker tags): Comment out unused secret variables in docker_tags.yaml 2024-11-16 22:31:16 +01:00
efd22d4087 4.3.13 2024-11-16 16:08:51 +01:00
c9e32540bf fix(package): Updated package dependencies 2024-11-16 16:08:50 +01:00
8344f96983 4.3.12 2024-11-06 21:26:23 +01:00
3b77089d79 fix(workflow): Fix Docker image path in GitHub action workflow 2024-11-06 21:26:23 +01:00
b6bce76043 4.3.11 2024-11-06 21:21:15 +01:00
cab57ab303 fix(overall): Refactor and improve code consistency across all modules 2024-11-06 21:21:14 +01:00
804f1f3b12 4.3.10 2024-11-06 21:16:12 +01:00
f0144fdd5b fix(dependencies): Updated dependencies and fixed Docker Alpine image retrieval issue in tests 2024-11-06 21:16:12 +01:00
81f286cb2f 4.3.9 2024-11-06 17:19:44 +01:00
1f12cb9f94 fix(test and dependencies): Corrected cloudlyUrl in test.apiclient and updated tapbundle dependency. 2024-11-06 17:19:43 +01:00
26490e8ddd 4.3.8 2024-11-06 16:59:18 +01:00
38d2120c35 fix(api client): Fixed localhost URL issue in test.client.ts 2024-11-06 16:59:17 +01:00
f80b8decbc 4.3.7 2024-11-06 16:53:22 +01:00
28cd6d1b49 fix(tests): Refactored test setup for consistency and isolated config initialization. 2024-11-06 16:53:21 +01:00
899e5b0a7d 4.3.6 2024-11-06 16:22:38 +01:00
0eff7c7510 fix(test): Enhance test helpers with dynamic Hetzner token retrieval. 2024-11-06 16:22:38 +01:00
7789348f4e 4.3.5 2024-11-06 16:13:55 +01:00
66a23a515b fix(helpers): Add missing sslMode configuration to Cloudly config. 2024-11-06 16:13:54 +01:00
7c1082f5a9 4.3.4 2024-11-06 03:56:46 +01:00
15ea5adec6 fix(testing): Fixed Cloudly testing setup and dependencies 2024-11-06 03:56:46 +01:00
da0dddcceb 4.3.3 2024-11-05 21:31:16 +01:00
b5433e412f fix(core): Fix configuration initialization by accepting a config argument 2024-11-05 21:31:15 +01:00
7eb6bf794c 4.3.2 2024-11-05 02:45:04 +01:00
b244518fcb fix(npmextra): Updated npm registry URL in npmextra.json 2024-11-05 02:45:04 +01:00
95d0396abb 4.3.1 2024-11-05 02:21:06 +01:00
a830299cc9 fix(package): Update dependency version for @git.zone/tspublish 2024-11-05 02:21:06 +01:00
10fc1d7fba 4.3.0 2024-11-05 02:06:44 +01:00
614ed78928 feat(dependencies): Upgrade dependencies and include publish orders 2024-11-05 02:06:44 +01:00
ea7f6a6477 4.2.1 2024-11-04 19:24:47 +01:00
2d746a9d1c fix(config): Fix Docker image URL in Gitea workflow. 2024-11-04 19:24:47 +01:00
a9fab24961 4.2.0 2024-11-04 19:23:45 +01:00
5548d5a72d feat(cloudron): Add Dockerfile for Cloudron deployment 2024-11-04 19:23:45 +01:00
6b52e05a86 4.1.3 2024-10-28 21:54:55 +01:00
87e273c30e fix(dependency): Updated dependency @git.zone/tspublish to version ^1.6.0 2024-10-28 21:54:55 +01:00
41 changed files with 8800 additions and 4812 deletions

@ -6,13 +6,14 @@ on:
- '*'
env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_LOGIN_DOCKER_GITEA: ${{ github.server_url }}|${{ gitea.repository_owner }}|${{ secrets.GITEA_TOKEN }}
# NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
# NPMCI_LOGIN_DOCKER_GITEA: ${{ github.server_url }}|${{ gitea.repository_owner }}|${{ secrets.GITEA_TOKEN }}
NPMCI_LOGIN_DOCKER_DOCKERREGISTRY: ${{ secrets.NPMCI_LOGIN_DOCKER_DOCKERREGISTRY }}
NPMCI_SECRET01: ${{ secrets.NPMCI_SECRET01 }}
jobs:
security:
@ -74,7 +75,7 @@ jobs:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: code.foss.global/hosttoday/ht-docker-dbase:npmci
image: code.foss.global/host.today/ht-docker-dbase:npmci
steps:
- uses: actions/checkout@v3
@ -89,8 +90,7 @@ jobs:
npmci docker login
npmci docker build
npmci docker test
# npmci docker push
npmci docker push
npmci docker push code.foss.global
metadata:
needs: test

@ -1,6 +1,6 @@
# gitzone dockerfile_service
## STAGE 1 // BUILD
FROM code.foss.global/hosttoday/ht-docker-node:npmci as node1
FROM code.foss.global/host.today/ht-docker-node:npmci as node1
COPY ./ /app
WORKDIR /app
ARG NPMCI_TOKEN_NPM2
@ -12,7 +12,7 @@ RUN pnpm run build
# gitzone dockerfile_service
## STAGE 2 // install production
FROM code.foss.global/hosttoday/ht-docker-node:npmci as node2
FROM code.foss.global/host.today/ht-docker-node:npmci as node2
WORKDIR /app
COPY --from=node1 /app /app
RUN rm -rf .pnpm-store
@ -24,7 +24,7 @@ RUN rm -rf node_modules/ && pnpm install --prod
## STAGE 3 // rebuild dependencies for alpine
FROM code.foss.global/hosttoday/ht-docker-node:alpinenpmci as node3
FROM code.foss.global/host.today/ht-docker-node:alpine_npmci as node3
WORKDIR /app
COPY --from=node2 /app /app
ARG NPMCI_TOKEN_NPM2
@ -34,7 +34,7 @@ RUN pnpm config set store-dir .pnpm-store
RUN pnpm rebuild -r
## STAGE 4 // the final production image with all dependencies in place
FROM code.foss.global/hosttoday/ht-docker-node:alpine as node4
FROM code.foss.global/host.today/ht-docker-node:alpine as node4
WORKDIR /app
COPY --from=node3 /app /app

@ -0,0 +1 @@
FROM serve.zone/cloudly:latest

@ -1,5 +1,195 @@
# Changelog
## 2024-12-21 - 4.5.4 - fix(ts_web)
Fix action type and data fields in appstate for CRUD operations
- Correct request method in createSecretGroupAction to accurately reflect the purpose.
- Align the deleteSecretGroupAction and deleteSecretBundleAction request types with proper interfaces.
- Ensure data payload matches backend requirements for secret group and secret bundle operations.
## 2024-12-21 - 4.5.3 - fix(secret-management)
Refactor secret management to use distinct secret bundle and group APIs. Introduce API client classes for secret bundles and groups.
- Updated secret management logic to separate secret bundle and group APIs.
- Implemented new API client classes for managing secret bundles and groups.
- Fixed incorrect method usages for secret-related actions.
## 2024-12-20 - 4.5.2 - fix(apiclient)
Implemented IService interface in Service class and improved secret bundle documentation.
- Implemented plugins.servezoneInterfaces.data.IService interface in the Service class within ts_apiclient.
- Updated documentation comments for the type property in the ISecretBundle interface.
## 2024-12-17 - 4.5.1 - fix(core)
Updated dependencies in package.json to latest versions.
- Bumped @git.zone/tswatch to version ^2.0.37
- Bumped @types/node to version ^22.10.2
- Bumped @design.estate/dees-catalog to version ^1.3.2
- Bumped @push.rocks/smartfile to version ^11.0.23
- Bumped @tsclass/tsclass to version ^4.2.0
## 2024-12-14 - 4.5.0 - feat(services)
Add service management functionalities
- Integrated service-related API client methods including getServices, getServiceById, and createService.
- Updated the deployment data structure in the service manager.
- Enhanced service interface to incorporate additional fields for comprehensive data handling.
- Ensured secure token generation for Cloudly authentication processes.
## 2024-11-18 - 4.4.0 - feat(api-client)
Add static method getImageById for Image class in api-client
- Introduced a static method getImageById in the Image class.
- Updated CloudlyApiClient to include the getImageById method in the images interface.
## 2024-11-18 - 4.3.21 - fix(interfaces)
Remove deprecated deployment directive and update related interfaces
- Removed IDeploymentDirective from data and requests.
- Updated IDeployment to remove references to directives.
- Changed IRequest_Any_Cloudly_GetClusterConfig to return services instead of deployment directives.
- Removed deploymentDirectiveIds from IService data structure.
## 2024-11-18 - 4.3.20 - fix(apiclient)
Ensure mandatory parameter in CloudlyApiClient constructor
- Made the 'optionsArg' parameter mandatory in the constructor of CloudlyApiClient class.
## 2024-11-18 - 4.3.19 - fix(docker)
Fix improper Docker push command preventing push to the correct registry.
- Corrected the docker push command in the '.gitea/workflows/docker_tags.yaml' file to push images to the 'code.foss.global' registry.
## 2024-11-17 - 4.3.18 - fix(docker_tags)
Updated Docker configuration to include NPM tokens.
- Restored NPMCI_TOKEN_NPM and NPMCI_TOKEN_NPM2 environment variables in docker_tags.yaml for authentication purposes.
## 2024-11-17 - 4.3.17 - fix(Dockerfile)
Corrected docker base image tag in Dockerfile for alpine compatibility.
- Updated Dockerfile to use the correct base image tag for Alpine.
- Resolved any potential build issues related to incorrect image tag usage.
## 2024-11-17 - 4.3.16 - fix(infrastructure)
Correct Docker image path in Dockerfile for improved build consistency.
- Dockerfile: Updated base image paths from 'code.foss.global/hosttoday/ht-docker-node' to 'code.foss.global/host.today/ht-docker-node'.
## 2024-11-17 - 4.3.15 - fix(project setup)
fixed incorrect configuration in npmextra.json
- Removed unnecessary 'dockerBuildargEnvMap' entry for NPMCI_TOKEN_NPM2.
- Corrected the githost and gitscope in gitzone module configuration.
- Updated the license field to reflect the correct license.
## 2024-11-16 - 4.3.14 - fix(docker tags)
Comment out unused secret variables in docker_tags.yaml
- Modified docker_tags.yaml to comment out unused secret variables related to NPM and GitHub tokens.
## 2024-11-16 - 4.3.13 - fix(package)
Updated package dependencies
- Updated @design.estate/dees-catalog version to 1.3.1
## 2024-11-06 - 4.3.12 - fix(workflow)
Fix Docker image path in GitHub action workflow
- Corrected the path of the Docker image used in the GitHub action workflow from 'code.foss.global/hosttoday/ht-docker-dbase:npmci' to 'code.foss.global/host.today/ht-docker-dbase:npmci'.
## 2024-11-06 - 4.3.11 - fix(overall)
Refactor and improve code consistency across all modules
- Updated cloud configuration management for better reliability.
- Enhanced security measures in the authentication and authorization processes.
- Streamlined deployment logic in cluster management.
- Refactored code to improve maintainability and readability.
## 2024-11-06 - 4.3.10 - fix(dependencies)
Updated dependencies and fixed Docker Alpine image retrieval issue in tests
- Updated @push.rocks/tapbundle to version ^5.5.0 in devDependencies.
- Updated @push.rocks/smartrequest to version ^2.0.23 in dependencies.
- Ensured the Docker Alpine image is retrieved as a local tarball in cloudlyfactory.ts test helper.
## 2024-11-06 - 4.3.9 - fix(test and dependencies)
Corrected cloudlyUrl in test.apiclient and updated tapbundle dependency.
- Fixed cloudlyUrl assignment in the test.apiclient to use the correct helper method.
- Updated tapbundle dependency version from ^5.4.3 to ^5.4.4.
## 2024-11-06 - 4.3.8 - fix(api client)
Fixed localhost URL issue in test.client.ts
- Changed the cloudlyUrl in test.client.ts from 'localhost' to '127.0.0.1' to ensure consistency in network requests.
## 2024-11-06 - 4.3.7 - fix(tests)
Refactored test setup for consistency and isolated config initialization.
- test/helpers/cloudlyfactory.ts: Test configuration setup was refactored to ensure consistent initialization of cloudly configuration across tests.
- test/test.apiclient.ts: Updated cloudlyApiClient test setup to use testCloudlyConfig for dynamic port allocation.
## 2024-11-06 - 4.3.6 - fix(test)
Enhance test helpers with dynamic Hetzner token retrieval.
- Updated test/helpers/cloudlyfactory.ts to retrieve Hetzner token from environment variables.
- Expanded docker_tags workflow to securely handle and pass new environment secrets.
## 2024-11-06 - 4.3.5 - fix(helpers)
Add missing sslMode configuration to Cloudly config.
- Added the sslMode key with a value of 'none' to the Cloudly configuration object in test/helpers/cloudlyfactory.ts.
## 2024-11-06 - 4.3.4 - fix(testing)
Fixed Cloudly testing setup and dependencies
- Updated devDependency @push.rocks/tapbundle version from ^5.3.0 to ^5.4.3 in package.json.
- Updated devDependency @push.rocks/npmextra version from ^5.1.1 to ^5.1.2 in package.json.
- Improved the Cloudly test suite setup to ensure proper initialization of MongoDB and S3 services.
- Ensured asynchronous stopping and cleanup of MongoDB and S3 services post-test execution.
## 2024-11-05 - 4.3.3 - fix(core)
Fix configuration initialization by accepting a config argument
- Configuration initialization now accepts an optional config argument
- Updated test cloudly factory to use default public URL and port
- Updated dependencies versions
## 2024-11-05 - 4.3.2 - fix(npmextra)
Updated npm registry URL in npmextra.json
## 2024-11-05 - 4.3.1 - fix(package)
Update dependency version for @git.zone/tspublish
- Bump @git.zone/tspublish from version ^1.7.6 to ^1.7.7 in package.json
## 2024-11-05 - 4.3.0 - feat(dependencies)
Upgrade dependencies and include publish orders
- Upgraded @git.zone/tsbuild to version ^2.2.0
- Upgraded @git.zone/tspublish to version ^1.7.6
- Upgraded @types/node to version ^22.8.7
- Added publish order to ts_apiclient/tspublish.json and ts_interfaces/tspublish.json
## 2024-11-04 - 4.2.1 - fix(config)
Fix Docker image URL in Gitea workflow.
- Corrected the IMAGE URL from 'hosttoday' to 'host.today'.
## 2024-11-04 - 4.2.0 - feat(cloudron)
Add Dockerfile for Cloudron deployment
- Introduced a new Dockerfile for Cloudron deployment.
- The Dockerfile uses the latest version of cloudly as a base image.
## 2024-10-28 - 4.1.3 - fix(dependency)
Updated dependency @git.zone/tspublish to version ^1.6.0
- Bumped @git.zone/tspublish version from ^1.5.5 to ^1.6.0 in package.json
## 2024-10-28 - 4.1.2 - fix(core)
Corrected description and devDependencies

19
license Normal file

@ -0,0 +1,19 @@
Copyright (c) 2014 Task Venture Capital GmbH (hello@task.vc)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -2,23 +2,21 @@
"npmci": {
"npmGlobalTools": [],
"npmAccessLevel": "public",
"npmRegistryUrl": "verdaccio.lossless.one",
"npmRegistryUrl": "verdaccio.lossless.digital",
"dockerRegistryRepoMap": {
"registry.gitlab.com": "losslessone/services/servezone/cloudly"
"code.foss.global": "serve.zone/cloudly"
},
"dockerBuildargEnvMap": {
"NPMCI_TOKEN_NPM2": "NPMCI_TOKEN_NPM2"
}
"dockerBuildargEnvMap": {}
},
"gitzone": {
"projectType": "service",
"module": {
"githost": "gitlab.com",
"gitscope": "servezone/private",
"githost": "code.foss.global",
"gitscope": "serve.zone",
"gitrepo": "cloudly",
"description": "A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.",
"npmPackagename": "@serve.zone/cloudly",
"license": "UNLICENSED",
"license": "MIT",
"keywords": [
"multi-cloud management",
"Docker Swarmkit",

@ -1,6 +1,6 @@
{
"name": "@serve.zone/cloudly",
"version": "4.1.2",
"version": "4.5.4",
"private": false,
"description": "A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.",
"type": "module",
@ -22,14 +22,14 @@
"docs": "tsdoc aidoc"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.85",
"@git.zone/tsbuild": "^2.2.0",
"@git.zone/tsbundle": "^2.1.0",
"@git.zone/tsdoc": "^1.4.2",
"@git.zone/tspublish": "^1.5.5",
"@git.zone/tspublish": "^1.7.7",
"@git.zone/tstest": "^1.0.90",
"@git.zone/tswatch": "^2.0.25",
"@push.rocks/tapbundle": "^5.3.0",
"@types/node": "^22.8.2"
"@git.zone/tswatch": "^2.0.37",
"@push.rocks/tapbundle": "^5.5.3",
"@types/node": "^22.10.2"
},
"dependencies": {
"@api.global/typedrequest": "3.1.10",
@ -40,23 +40,23 @@
"@apiclient.xyz/docker": "^1.2.7",
"@apiclient.xyz/hetznercloud": "^1.2.0",
"@apiclient.xyz/slack": "^3.0.9",
"@design.estate/dees-catalog": "^1.2.0",
"@design.estate/dees-catalog": "^1.3.2",
"@design.estate/dees-domtools": "^2.0.64",
"@design.estate/dees-element": "^2.0.39",
"@git.zone/tsrun": "^1.3.3",
"@push.rocks/early": "^4.0.3",
"@push.rocks/npmextra": "^5.0.23",
"@push.rocks/npmextra": "^5.1.2",
"@push.rocks/projectinfo": "^5.0.1",
"@push.rocks/qenv": "^6.0.5",
"@push.rocks/qenv": "^6.1.0",
"@push.rocks/smartacme": "^5.0.0",
"@push.rocks/smartbucket": "^3.0.23",
"@push.rocks/smartbucket": "^3.3.7",
"@push.rocks/smartcli": "^4.0.11",
"@push.rocks/smartclickhouse": "^2.0.17",
"@push.rocks/smartdata": "^5.2.10",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartexit": "^1.0.23",
"@push.rocks/smartexpect": "^1.2.1",
"@push.rocks/smartfile": "^11.0.21",
"@push.rocks/smartfile": "^11.0.23",
"@push.rocks/smartguard": "^3.1.0",
"@push.rocks/smartjson": "^5.0.19",
"@push.rocks/smartjwt": "^2.2.1",
@ -65,16 +65,16 @@
"@push.rocks/smartlog-interfaces": "^3.0.2",
"@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.4",
"@push.rocks/smartrequest": "^2.0.22",
"@push.rocks/smartrequest": "^2.0.23",
"@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartssh": "^2.0.1",
"@push.rocks/smartstate": "^2.0.19",
"@push.rocks/smartstream": "^3.2.4",
"@push.rocks/smartstream": "^3.2.5",
"@push.rocks/smartstring": "^4.0.15",
"@push.rocks/smartunique": "^3.0.9",
"@push.rocks/taskbuffer": "^3.0.2",
"@push.rocks/webjwt": "^1.0.9",
"@tsclass/tsclass": "^4.1.2"
"@tsclass/tsclass": "^4.2.0"
},
"files": [
"ts/**/*",

12022
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

@ -3,25 +3,48 @@ const testQenv = new Qenv('./', './.nogit/');
import * as cloudly from '../../ts/index.js';
export const createCloudly = async () => {
const cloudlyConfig: cloudly.ICloudlyConfig = {
const stopFunctions: Array<() => Promise<void>> = [];
const tapToolsNodeMod = await import('@push.rocks/tapbundle/node');
const smartmongo = await tapToolsNodeMod.tapNodeTools.createSmartmongo();
stopFunctions.push(async () => {
await smartmongo.stopAndDumpToDir('./.nogit/mongodump');
});
const smarts3 = await tapToolsNodeMod.tapNodeTools.createSmarts3();
await smarts3.createBucket('cloudly-test');
stopFunctions.push(async () => {
await smarts3.stop();
});
export const testCloudlyConfig: cloudly.ICloudlyConfig = {
cfToken: await testQenv.getEnvVarOnDemand('CF_TOKEN'),
environment: 'integration',
letsEncryptEmail: await testQenv.getEnvVarOnDemand('LETSENCRYPT_EMAIL'),
publicUrl: await testQenv.getEnvVarOnDemand('SERVEZONE_URL'),
publicPort: await testQenv.getEnvVarOnDemand('SERVEZONE_PORT'),
mongoDescriptor: {
mongoDbName: await testQenv.getEnvVarOnDemand('MONGODB_DATABASE'),
mongoDbUser: await testQenv.getEnvVarOnDemand('MONGODB_USER'),
mongoDbPass: await testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGODB_URL'),
},
letsEncryptEmail: 'test@serve.zone',
publicUrl: '127.0.0.1',
publicPort: '8080',
mongoDescriptor: await smartmongo.getMongoDescriptor(),
s3Descriptor: await smarts3.getS3Descriptor(),
sslMode: 'none',
...(() => {
if (process.env.NPMCI_SECRET01) {
return {
hetznerToken: process.env.NPMCI_SECRET01,
};
const cloudlyInstance = new cloudly.Cloudly();
}
})(),
};
await tapToolsNodeMod.tapNodeTools.testFileProvider.getDockerAlpineImageAsLocalTarball();
export const createCloudly = async () => {
const cloudlyInstance = new cloudly.Cloudly(testCloudlyConfig);
return cloudlyInstance;
}
};
export const stopCloudly = async () => {
await Promise.all(stopFunctions.map((stopFunction) => stopFunction()));
};
export const getEnvVarOnDemand = async (envVarName: string) => {
return testQenv.getEnvVarOnDemand(envVarName);
}
};

@ -33,7 +33,7 @@ tap.preTask('should create a new machine user for testing', async () => {
tap.test('should create a new cloudlyApiClient', async () => {
testClient = new cloudlyApiClient.CloudlyApiClient({
registerAs: 'api',
cloudlyUrl: `http://localhost:${await helpers.getEnvVarOnDemand('SERVEZONE_PORT')}`,
cloudlyUrl: `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`,
});
await testClient.start();
expect(testClient).toBeTruthy();
@ -73,6 +73,7 @@ tap.test('should upload an image version', async () => {
tap.test('should stop the apiclient', async (toolsArg) => {
await toolsArg.delayFor(10000);
await helpers.stopCloudly();
await testClient.stop();
await testCloudly.stop();
})

@ -14,8 +14,10 @@ tap.test('should init cloudly', async () => {
});
tap.test('should end the service', async (tools) => {
await tools.delayFor(5000);
await helpers.stopCloudly();
await testCloudly.stop();
tools.delayFor(1000).then(() => process.exit())
tools.delayFor(1000).then(() => process.exit());
});
tap.start();

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/cloudly',
version: '4.1.2',
version: '4.5.4',
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
}

@ -60,7 +60,9 @@ export class Cloudly {
private readyDeferred = new plugins.smartpromise.Deferred();
constructor() {
private configOptions: plugins.servezoneInterfaces.data.ICloudlyConfig;
constructor(configArg?: plugins.servezoneInterfaces.data.ICloudlyConfig) {
this.configOptions = configArg;
this.cloudlyInfo = new CloudlyInfo(this);
this.config = new CloudlyConfig(this);
@ -90,7 +92,7 @@ export class Cloudly {
*/
public async start() {
// config
await this.config.init();
await this.config.init(this.configOptions);
// manageers
await this.authManager.start();

@ -15,7 +15,7 @@ export class CloudlyConfig {
this.cloudlyRef = cloudlyRefArg;
}
public async init() {
public async init(configArg?: plugins.servezoneInterfaces.data.ICloudlyConfig) {
this.appData =
await plugins.npmextra.AppData.createAndInit<plugins.servezoneInterfaces.data.ICloudlyConfig>(
{
@ -54,6 +54,7 @@ export class CloudlyConfig {
'environment',
'mongoDescriptor',
],
overwriteObject: configArg,
},
);

@ -35,20 +35,71 @@ export class CloudlySecretManager {
this.typedrouter = new plugins.typedrequest.TypedRouter();
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_GetConfigBundlesAndSecretGroups>(
'adminGetConfigBundlesAndSecretGroups',
// secretbundle routes
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_GetSecretBundles>(
new plugins.typedrequest.TypedHandler(
'getSecretBundles',
async (dataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], dataArg);
dataArg.identity.jwt;
const secretBundles = await SecretBundle.getInstances({});
const secretGroups = await SecretGroup.getInstances({});
return {
secretBundles: [
...(await Promise.all(
secretBundles.map((configBundle) => configBundle.createSavableObject()),
)),
],
};
},
),
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_CreateSecretBundle>(
new plugins.typedrequest.TypedHandler('createSecretBundle', async (dataArg) => {
const secretBundle = new SecretBundle();
secretBundle.id = plugins.smartunique.shortId(8);
secretBundle.data = dataArg.secretBundle.data;
await secretBundle.save();
return {
resultSecretBundle: await secretBundle.createSavableObject(),
};
}),
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_UpdateSecretBundle>(
new plugins.typedrequest.TypedHandler('updateSecretBundle', async (dataArg) => {
const secretBundle = await SecretBundle.getInstance({
id: dataArg.secretBundle.id,
});
secretBundle.data = dataArg.secretBundle.data;
await secretBundle.save();
return {
resultSecretBundle: await secretBundle.createSavableObject(),
};
}),
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_DeleteSecretBundleById>(
new plugins.typedrequest.TypedHandler('deleteSecretBundleById', async (dataArg) => {
const secretBundle = await SecretBundle.getInstance({
id: dataArg.secretBundleId,
});
await secretBundle.delete();
return {
ok: true,
};
}),
);
// secretgroup routes
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretgroup.IReq_GetSecretGroups>(
new plugins.typedrequest.TypedHandler(
'getSecretGroups',
async (dataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], dataArg);
dataArg.identity.jwt;
const secretGroups = await SecretGroup.getInstances({});
return {
secretGroups: [
...(await Promise.all(
secretGroups.map((secretGroup) => secretGroup.createSavableObject()),
@ -59,50 +110,45 @@ export class CloudlySecretManager {
),
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_CreateConfigBundlesAndSecretGroups>(
new plugins.typedrequest.TypedHandler(
'adminCreateConfigBundlesAndSecretGroups',
async (dataArg) => {
for (const secretGroupObject of dataArg.secretGroups) {
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretgroup.IReq_CreateSecretGroup>(
new plugins.typedrequest.TypedHandler('createSecretGroup', async (dataArg) => {
const secretGroup = new SecretGroup();
secretGroup.id = plugins.smartunique.shortId(8);
secretGroup.data = secretGroupObject.data;
secretGroup.data = dataArg.secretGroup.data;
await secretGroup.save();
}
return {
ok: true,
resultSecretGroup: await secretGroup.createSavableObject(),
};
},
),
}),
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_DeleteConfigBundlesAndSecretGroups>(
'adminDeleteConfigBundlesAndSecretGroups',
async (dataArg) => {
for (const secretGroupId of dataArg.secretGroupIds) {
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretgroup.IReq_UpdateSecretGroup>(
new plugins.typedrequest.TypedHandler('updateSecretGroup', async (dataArg) => {
const secretGroup = await SecretGroup.getInstance({
id: secretGroupId,
id: dataArg.secretGroup.id,
});
secretGroup.data = dataArg.secretGroup.data;
await secretGroup.save();
return {
resultSecretGroup: await secretGroup.createSavableObject(),
};
}),
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretgroup.IReq_DeleteSecretGroupById>(
new plugins.typedrequest.TypedHandler('deleteSecretGroupById', async (dataArg) => {
const secretGroup = await SecretGroup.getInstance({
id: dataArg.secretGroupId,
});
await secretGroup.delete();
}
for (const secretBundleId of dataArg.secretBundleIds) {
const configBundle = await SecretBundle.getInstance({
id: secretBundleId,
});
await configBundle.delete();
console.log(`deleted configbundle ${secretBundleId}`);
}
return {
ok: true,
};
},
),
}),
);
// lets add typedrouter routes for accessing the configvailt from apps
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_GetEnvBundle>(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_GetEnvBundle>(
'getEnvBundle',
async (dataArg) => {
const wantedBundle = await SecretBundle.getInstance({

@ -14,5 +14,26 @@ export class ServiceManager {
constructor(cloudlyRef: Cloudly) {
this.cloudlyRef = cloudlyRef;
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServices>(
'getServices',
async (reqArg) => {
await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const services = await this.CService.getInstances({});
return {
services: await Promise.all(
services.map((service) => {
return service.createSavableObject();
})
),
};
}
)
);
}
}

@ -3,6 +3,7 @@ import * as plugins from './plugins.js';
export type TClientType = 'api' | 'ci' | 'coreflow' | 'cli' | 'serverconfig';
import { Image } from './classes.image.js';
import { Service } from './classes.service.js';
export class CloudlyApiClient {
private cloudlyUrl: string;
@ -20,7 +21,7 @@ export class CloudlyApiClient {
plugins.servezoneInterfaces.requests.server.IRequest_TriggerServerAction['request']
>();
constructor(optionsArg?: {
constructor(optionsArg: {
registerAs: TClientType;
cloudlyUrl?: string;
}) {
@ -157,6 +158,9 @@ export class CloudlyApiClient {
public images = {
// Images
getImageById: async (imageIdArg: string) => {
return Image.getImageById(this, imageIdArg);
},
getImages: async () => {
return Image.getImages(this);
},
@ -164,4 +168,17 @@ export class CloudlyApiClient {
return Image.createImage(this, optionsArg);
}
}
public services = {
// Services
getServiceById: async (serviceIdArg: string) => {
return Service.getServiceById(this, serviceIdArg);
},
getServices: async () => {
return Service.getServices(this);
},
createService: async (optionsArg: Parameters<typeof Service.createService>[1]) => {
return Service.createService(this, optionsArg);
}
}
}

@ -18,6 +18,19 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
return resultImages;
}
public static async getImageById(cloudlyClientRef: CloudlyApiClient, imageIdArg: string) {
const getImageByIdTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>(
'getImage'
);
const response = await getImageByIdTR.fire({
identity: cloudlyClientRef.identity,
imageId: imageIdArg,
});
const newImage = new Image(cloudlyClientRef);
Object.assign(newImage, response.image);
return newImage;
}
/**
* creates a new image
*/

@ -0,0 +1,96 @@
import * as plugins from './plugins.js';
import type { CloudlyApiClient } from './classes.cloudlyapiclient.js';
export class SecretBundle implements plugins.servezoneInterfaces.data.ISecretBundle {
public cloudlyClientRef: CloudlyApiClient;
public id: string;
public data: plugins.servezoneInterfaces.data.ISecretBundle['data'];
constructor(cloudlyClientRef: CloudlyApiClient) {
this.cloudlyClientRef = cloudlyClientRef;
}
public static async getSecretBundleById(cloudlyClientRef: CloudlyApiClient, secretBundleIdArg: string) {
const getSecretBundleByIdTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretbundle.IReq_GetSecretBundleById>(
'getSecretBundleById'
);
const response = await getSecretBundleByIdTR.fire({
identity: cloudlyClientRef.identity,
secretBundleId: secretBundleIdArg,
});
const newSecretBundle = new SecretBundle(cloudlyClientRef);
Object.assign(newSecretBundle, response.secretBundle);
return newSecretBundle;
}
public static async getSecretBundles(cloudlyClientRef: CloudlyApiClient) {
const getSecretBundlesTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretbundle.IReq_GetSecretBundles>(
'getSecretBundles'
);
const response = await getSecretBundlesTR.fire({
identity: cloudlyClientRef.identity,
});
const secretBundles: SecretBundle[] = [];
for (const secretBundle of response.secretBundles) {
const newSecretBundle = new SecretBundle(cloudlyClientRef);
Object.assign(newSecretBundle, secretBundle);
secretBundles.push(newSecretBundle);
}
return secretBundles;
}
public static async createSecretBundle(cloudlyClientRef: CloudlyApiClient, secretBundleDataArg: Partial<plugins.servezoneInterfaces.data.ISecretBundle['data']>) {
const createSecretBundleTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretbundle.IReq_CreateSecretBundle>(
'createSecretBundle'
);
const response = await createSecretBundleTR.fire({
identity: cloudlyClientRef.identity,
secretBundle: {
id: null,
data: {
name: secretBundleDataArg.name,
description: secretBundleDataArg.description,
type: secretBundleDataArg.type,
authorizations: secretBundleDataArg.authorizations,
includedImages: secretBundleDataArg.includedImages,
includedSecretGroupIds: secretBundleDataArg.includedSecretGroupIds,
includedTags: secretBundleDataArg.includedTags,
},
},
});
const newSecretBundle = new SecretBundle(cloudlyClientRef);
Object.assign(newSecretBundle, response.resultSecretBundle);
return newSecretBundle;
}
public async update() {
const updateSecretBundleTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretbundle.IReq_UpdateSecretBundle>(
'updateSecretBundle'
);
const response = await updateSecretBundleTR.fire({
identity: this.cloudlyClientRef.identity,
secretBundle: {
id: this.id,
data: this.data,
},
});
const resultSecretBundleData = response.resultSecretBundle.data;
plugins.smartexpect.expect(resultSecretBundleData).toEqual(this.data);
return this;
}
public async delete(cloudlyClientRef: CloudlyApiClient, secretBundleIdArg: string) {
const deleteSecretBundleTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretbundle.IReq_DeleteSecretBundleById>(
'deleteSecretBundleById'
);
const response = await deleteSecretBundleTR.fire({
identity: cloudlyClientRef.identity,
secretBundleId: this.id,
});
plugins.smartexpect.expect(response.ok).toBeTrue();
return null;
}
}

@ -0,0 +1,103 @@
import * as plugins from './plugins.js';
import type { CloudlyApiClient } from './classes.cloudlyapiclient.js';
export class SecretGroup implements plugins.servezoneInterfaces.data.ISecretGroup {
public cloudlyClientRef: CloudlyApiClient;
public id: string;
public data: plugins.servezoneInterfaces.data.ISecretGroup['data'];
constructor(cloudlyClientRef: CloudlyApiClient) {
this.cloudlyClientRef = cloudlyClientRef;
}
public static async getSecretGroupById(cloudlyClientRef: CloudlyApiClient, secretGroupIdArg: string) {
const getSecretGroupByIdTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretgroup.IReq_GetSecretGroupById>(
'getSecretGroupById'
);
const response = await getSecretGroupByIdTR.fire({
identity: cloudlyClientRef.identity,
secretGroupId: secretGroupIdArg,
});
const newSecretGroup = new SecretGroup(cloudlyClientRef);
Object.assign(newSecretGroup, response.secretGroup);
return newSecretGroup;
}
public static async getSecretGroups(cloudlyClientRef: CloudlyApiClient) {
const getSecretGroupsTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretgroup.IReq_GetSecretGroups>(
'getSecretGroups'
);
const response = await getSecretGroupsTR.fire({
identity: cloudlyClientRef.identity,
});
const secretGroups: SecretGroup[] = [];
for (const secretGroup of response.secretGroups) {
const newSecretGroup = new SecretGroup(cloudlyClientRef);
Object.assign(newSecretGroup, secretGroup);
secretGroups.push(newSecretGroup);
}
return secretGroups;
}
public static async createSecretGroup(cloudlyClientRef: CloudlyApiClient, secretGroupDataArg: Partial<plugins.servezoneInterfaces.data.ISecretGroup['data']>) {
const createSecretGroupTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretgroup.IReq_CreateSecretGroup>(
'createSecretGroup'
);
const response = await createSecretGroupTR.fire({
identity: cloudlyClientRef.identity,
secretGroup: {
id: null,
data: {
name: secretGroupDataArg.name,
description: secretGroupDataArg.description,
environments: secretGroupDataArg.environments,
key: secretGroupDataArg.key,
tags: secretGroupDataArg.tags,
priority: secretGroupDataArg.priority,
},
},
});
const newSecretGroup = new SecretGroup(cloudlyClientRef);
Object.assign(newSecretGroup, response.resultSecretGroup);
return newSecretGroup;
}
// INSTANCE
public async update() {
const updateSecretGroupTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretgroup.IReq_UpdateSecretGroup>(
'updateSecretGroup'
);
const response = await updateSecretGroupTR.fire({
identity: this.cloudlyClientRef.identity,
secretGroup: {
id: this.id,
data: {
name: this.data.name,
description: this.data.description,
environments: this.data.environments,
key: this.data.key,
tags: this.data.tags,
priority: this.data.priority,
},
},
});
const resultSecretGroupData = response.resultSecretGroup.data;
plugins.smartexpect.expect(resultSecretGroupData).toEqual(this.data);
return this;
}
public async delete(cloudlyClientRef: CloudlyApiClient, secretGroupIdArg: string) {
const deleteSecretGroupTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.secretgroup.IReq_DeleteSecretGroupById>(
'deleteSecretGroupById'
);
const response = await deleteSecretGroupTR.fire({
identity: cloudlyClientRef.identity,
secretGroupId: this.id,
});
plugins.smartexpect.expect(response.ok).toBeTrue();
return null;
}
}

@ -0,0 +1,27 @@
import * as plugins from './plugins.js';
import type { CloudlyApiClient } from './classes.cloudlyapiclient.js';
import { SecretBundle } from './classes.secretbundle.js';
import { SecretGroup } from './classes.secretgroup.js';
export class SecretManager {
// INSTANCE
cloudlyClientRef: CloudlyApiClient;
constructor(cloudlyClientRef: CloudlyApiClient) {
this.cloudlyClientRef = cloudlyClientRef;
}
public async getSecretGroupsAndBundles() {
}
/**
* The secret group has a secret bundle.
* This function essentially returns the secret bundle as a flat object.
* In other words, it resolves secret groups and
*/
public async getSecretBundleAsFlatObject(environmentArg: string = 'production') {
}
}

@ -1,5 +1,80 @@
import * as plugins from './plugins.js';
import type { CloudlyApiClient } from './classes.cloudlyapiclient.js';
export class Service {
export class Service implements plugins.servezoneInterfaces.data.IService {
public static async getServices(cloudlyClientRef: CloudlyApiClient) {
const getAllServicesTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServices>(
'getServices'
);
const response = await getAllServicesTR.fire({
identity: cloudlyClientRef.identity,
});
const resultServices: Service[] = [];
for (const service of response.services) {
const newService = new Service(cloudlyClientRef);
Object.assign(newService, service);
resultServices.push(newService);
}
return resultServices;
}
public static async getServiceById(cloudlyClientRef: CloudlyApiClient, serviceIdArg: string) {
const getServiceByIdTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceById>(
'getServiceById'
);
const response = await getServiceByIdTR.fire({
identity: cloudlyClientRef.identity,
serviceId: serviceIdArg,
});
const newService = new Service(cloudlyClientRef);
Object.assign(newService, response.service);
return newService;
}
/**
* creates a new service
*/
public static async createService(cloudlyClientRef: CloudlyApiClient, serviceDataArg: Partial<plugins.servezoneInterfaces.data.IService['data']>) {
const createServiceTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_CreateService>(
'createService'
);
const response = await createServiceTR.fire({
identity: cloudlyClientRef.identity,
name: serviceDataArg.name,
description: serviceDataArg.description,
imageId: serviceDataArg.imageId,
imageVersion: serviceDataArg.imageVersion,
environment: {},
secretBundleId: null,
scaleFactor: 1,
balancingStrategy: serviceDataArg.balancingStrategy,
ports: {
web: null,
},
resources: serviceDataArg.resources,
domains: [],
});
const newService = new Service(cloudlyClientRef);
Object.assign(newService, response.service);
return newService;
}
// INSTANCE
cloudlyClientRef: CloudlyApiClient;
public id: string;
public data: plugins.servezoneInterfaces.data.IService['data'];
constructor(cloudlyClientRef: CloudlyApiClient) {
this.cloudlyClientRef = cloudlyClientRef;
}
/**
* The service has a secret bundle.
* This function essentially returns the secret bundle as a flat object.
* In other words, it resolves secret groups and
*/
public async getSecretBundleAsFlatObject(environmentArg: string = 'production') {
}
}

@ -6,11 +6,13 @@ export {
}
// @push.rocks scope
import * as smartexpect from '@push.rocks/smartexpect';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrx from '@push.rocks/smartrx';
import * as webstream from '@push.rocks/smartstream/web';
export {
smartexpect,
smartpromise,
smartrx,
webstream,

@ -1,4 +1,5 @@
{
"order": 1,
"name": "@serve.zone/api",
"dependencies": [
"@serve.zone/interfaces",

@ -1,12 +1,11 @@
import * as plugins from '../plugins.js';
/**
* results from a DeploymentDirective
* a deployment happens when a service is deployed
* tracks the status of a deployment
*/
export interface IDeployment {
id: string;
deploymentDirectiveId: string;
affectedServiceIds: string[];
usedImageId: string;
deploymentLog: string[];

@ -1,14 +0,0 @@
import type { IServiceRessources } from "./docker.js";
/**
* used for tellilng a cluster about a disired deployment
* and specifies its configuration
*/
export interface IDeploymentDirective {
id: string;
name: string;
imageClaim: string;
ports: { hostPort: number; containerPort: number }[];
environment: { [key: string]: string };
resources?: IServiceRessources;
}

@ -2,7 +2,6 @@ export * from './cloudlyconfig.js';
export * from './cluster.js';
export * from './config.js';
export * from './deployment.js';
export * from './deploymentdirective.js';
export * from './docker.js';
export * from './env.js';
export * from './event.js';

@ -7,6 +7,17 @@ export interface ISecretBundle {
/**
* determines if the secret is a service or an external secret
* if external secret additional checks are put in place to protect the secret
*
* * service:
* the bundle belongs to a service and can only be used by that service
* * npmci:
* the bundle is a secret bundle that is used by an npmci pipeline
* production secrets will be omitted in any case
* * gitzone:
* the bundle is a secret bundle that is used by a gitzone.
* Only local environment variables are allowed
* * external:
* the bundle is a secret bundle that is used by an external service
*/
type: 'service' | 'npmci' | 'gitzone' | 'external';

@ -4,6 +4,7 @@ export interface IService {
id: string;
data: {
name: string;
description: string;
imageId: string;
imageVersion: string;
environment: { [key: string]: string };
@ -21,6 +22,5 @@ export interface IService {
protocol?: 'http' | 'https' | 'ssh';
}[];
deploymentIds: string[];
deploymentDirectiveIds: string[];
};
}

@ -0,0 +1,16 @@
import * as plugins from '../plugins.js';
import * as userInterfaces from '../data/user.js';
export interface IReq_Admin_LoginWithUsernameAndPassword extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_Admin_LoginWithUsernameAndPassword
> {
method: 'adminLoginWithUsernameAndPassword';
request: {
username: string;
password: string;
};
response: {
identity: userInterfaces.IIdentity;
}
}

@ -3,7 +3,6 @@ import * as clusterInterfaces from '../data/cluster.js';
import * as serverInterfaces from '../data/server.js';
import * as userInterfaces from '../data/user.js';
import type { IService } from '../data/service.js';
import type { IDeploymentDirective } from '../data/deploymentdirective.js';
export interface IRequest_Any_Cloudly_GetServerConfig
extends plugins.typedrequestInterfaces.implementsTR<
@ -31,7 +30,7 @@ extends plugins.typedrequestInterfaces.implementsTR<
};
response: {
configData: clusterInterfaces.ICluster;
deploymentDirectives: IDeploymentDirective[];
services: IService[];
};
}
@ -43,7 +42,7 @@ extends plugins.typedrequestInterfaces.implementsTR<
method: 'pushClusterConfig';
request: {
configData: clusterInterfaces.ICluster;
deploymentDirectives: IDeploymentDirective[];
services: IService[];
};
response: {};
}

@ -18,6 +18,7 @@ export interface IRequest_GetAllImages extends plugins.typedrequestInterfaces.im
/**
* gets a single image
* authentication can happen via imageClaim or identity
*/
export interface IRequest_GetImage extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,

@ -1,5 +1,6 @@
import * as plugins from '../plugins.js';
import * as adminRequests from './admin.js';
import * as certificateRequests from './certificate.js';
import * as clusterRequests from './cluster.js';
import * as configRequests from './config.js';
@ -9,12 +10,15 @@ import * as informRequests from './inform.js';
import * as logRequests from './log.js';
import * as networkRequests from './network.js';
import * as routingRequests from './routing.js';
import * as secretRequests from './secret.js';
import * as secretBundleRequests from './secretbundle.js';
import * as secretGroupRequests from './secretgroup.js';
import * as serverRequests from './server.js';
import * as serviceRequests from './service.js';
import * as statusRequests from './status.js';
import * as versionRequests from './version.js';
export {
adminRequests as admin,
certificateRequests as certificate,
clusterRequests as cluster,
configRequests as config,
@ -24,8 +28,10 @@ export {
logRequests as log,
networkRequests as network,
routingRequests as routing,
secretRequests as secret,
secretBundleRequests as secretbundle,
secretGroupRequests as secretgroup,
serverRequests as server,
serviceRequests as service,
statusRequests as status,
versionRequests as version,
};

@ -1,94 +0,0 @@
import * as plugins from '../plugins.js';
import * as data from '../data/index.js';
import * as userInterfaces from '../data/user.js';
export interface IReq_GetEnvBundle extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetEnvBundle
> {
method: 'getEnvBundle';
request: {
authorization: string;
/**
* specify this if you want to get a warning, if the envBundle is for an unexpected environment
*/
environment?: string;
};
response: {
envBundle: data.IEnvBundle;
};
}
export interface IReq_Admin_LoginWithUsernameAndPassword extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_Admin_LoginWithUsernameAndPassword
> {
method: 'adminLoginWithUsernameAndPassword';
request: {
username: string;
password: string;
};
response: {
identity: userInterfaces.IIdentity;
}
}
export interface IReq_Admin_GetConfigBundlesAndSecretGroups extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_Admin_GetConfigBundlesAndSecretGroups
> {
method: 'adminGetConfigBundlesAndSecretGroups';
request: {
identity: userInterfaces.IIdentity;
};
response: {
secretBundles: data.ISecretBundle[];
secretGroups: data.ISecretGroup[];
};
}
export interface IReq_Admin_CreateConfigBundlesAndSecretGroups extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_Admin_CreateConfigBundlesAndSecretGroups
> {
method: 'adminCreateConfigBundlesAndSecretGroups';
request: {
identity: userInterfaces.IIdentity;
secretBundles: data.ISecretBundle[];
secretGroups: data.ISecretGroup[];
};
response: {
ok: boolean;
};
}
export interface IReq_Admin_UpdateConfigBundlesAndSecretGroups extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_Admin_UpdateConfigBundlesAndSecretGroups
> {
method: 'adminUpdateConfigBundlesAndSecretGroups';
request: {
identity: userInterfaces.IIdentity;
configBundles: data.ISecretBundle[];
secretGroups: data.ISecretGroup[];
};
response: {
ok: boolean;
};
}
export interface IReq_Admin_DeleteConfigBundlesAndSecretGroups extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_Admin_DeleteConfigBundlesAndSecretGroups
> {
method: 'adminDeleteConfigBundlesAndSecretGroups';
request: {
identity: userInterfaces.IIdentity;
secretBundleIds: string[];
secretGroupIds: string[];
};
response: {
ok: boolean;
};
}

@ -0,0 +1,94 @@
import * as plugins from '../plugins.js';
import * as data from '../data/index.js';
import * as userInterfaces from '../data/user.js';
/**
* when retrieving secrets for actual use, you do this in the form of an envBundle.
*/
export interface IReq_GetEnvBundle extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetEnvBundle
> {
method: 'getEnvBundle';
request: {
authorization: string;
/**
* specify this if you want to get a warning, if the envBundle is for an unexpected environment
*/
environment?: string;
};
response: {
envBundle: data.IEnvBundle;
};
}
export interface IReq_GetSecretBundles extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetSecretBundles
> {
method: 'getSecretBundles';
request: {
identity: userInterfaces.IIdentity;
};
response: {
secretBundles: data.ISecretBundle[];
};
}
export interface IReq_GetSecretBundleById extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetSecretBundleById
> {
method: 'getSecretBundleById';
request: {
identity: userInterfaces.IIdentity;
secretBundleId: string;
};
response: {
secretBundle: data.ISecretBundle;
};
}
export interface IReq_CreateSecretBundle extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_CreateSecretBundle
> {
method: 'createSecretBundle';
request: {
identity: userInterfaces.IIdentity;
secretBundle: data.ISecretBundle;
};
response: {
resultSecretBundle: data.ISecretBundle;
};
}
export interface IReq_UpdateSecretBundle extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_UpdateSecretBundle
> {
method: 'updateSecretBundle';
request: {
identity: userInterfaces.IIdentity;
secretBundle: data.ISecretBundle;
};
response: {
resultSecretBundle: data.ISecretBundle;
};
}
export interface IReq_DeleteSecretBundleById extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_DeleteSecretBundleById
> {
method: 'deleteSecretBundleById';
request: {
identity: userInterfaces.IIdentity;
secretBundleId: string;
};
response: {
ok: boolean;
};
}

@ -0,0 +1,74 @@
import * as plugins from '../plugins.js';
import * as data from '../data/index.js';
import * as userInterfaces from '../data/user.js';
export interface IReq_GetSecretGroups extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetSecretGroups
> {
method: 'getSecretGroups';
request: {
identity: userInterfaces.IIdentity;
};
response: {
secretGroups: data.ISecretGroup[];
};
}
export interface IReq_GetSecretGroupById extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetSecretGroupById
> {
method: 'getSecretGroupById';
request: {
identity: userInterfaces.IIdentity;
secretGroupId: string;
};
response: {
secretGroup: data.ISecretGroup;
};
}
export interface IReq_CreateSecretGroup extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_CreateSecretGroup
> {
method: 'createSecretGroup';
request: {
identity: userInterfaces.IIdentity;
secretGroup: data.ISecretGroup;
};
response: {
resultSecretGroup: data.ISecretGroup;
};
}
export interface IReq_UpdateSecretGroup extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_UpdateSecretGroup
> {
method: 'updateSecretGroup';
request: {
identity: userInterfaces.IIdentity;
secretGroup: data.ISecretGroup;
};
response: {
resultSecretGroup: data.ISecretGroup;
};
}
export interface IReq_DeleteSecretGroupById extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_DeleteSecretGroupById
> {
method: 'deleteSecretGroupById';
request: {
identity: userInterfaces.IIdentity;
secretGroupId: string;
};
response: {
ok: boolean;
};
}

@ -0,0 +1,113 @@
import * as plugins from '../plugins.js';
import type { IService } from '../data/service.js';
import type { IIdentity } from '../data/user.js';
import type { IServiceRessources } from '../data/docker.js';
export interface IRequest_Any_Cloudly_GetServiceById
extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IRequest_Any_Cloudly_GetServiceById
> {
method: 'getServiceById';
request: {
identity: IIdentity;
serviceId: string;
};
response: {
service: IService;
};
}
export interface IRequest_Any_Cloudly_GetServices
extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IRequest_Any_Cloudly_GetServices
> {
method: 'getServices';
request: {
identity: IIdentity;
};
response: {
services: IService[];
};
}
export interface IRequest_Any_Cloudly_CreateService
extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IRequest_Any_Cloudly_CreateService
> {
method: 'createService';
request: {
identity: IIdentity;
name: string;
description: string;
imageId: string;
imageVersion: string;
environment: { [key: string]: string };
secretBundleId: string;
scaleFactor: number;
balancingStrategy: 'round-robin' | 'least-connections';
ports: {
web: number;
custom?: { [domain: string]: string };
};
resources?: IServiceRessources;
domains: {
name: string;
port?: number;
protocol?: 'http' | 'https' | 'ssh';
}[];
};
response: {
service: IService;
};
}
export interface IRequest_Any_Cloudly_UpdateService
extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IRequest_Any_Cloudly_UpdateService
> {
method: 'updateService';
request: {
identity: IIdentity;
serviceId: string;
name: string;
description: string;
imageId: string;
imageVersion: string;
environment: { [key: string]: string };
secretBundleId: string;
scaleFactor: number;
balancingStrategy: 'round-robin' | 'least-connections';
ports: {
web: number;
custom?: { [domain: string]: string };
};
resources?: IServiceRessources;
domains: {
name: string;
port?: number;
protocol?: 'http' | 'https' | 'ssh';
}[];
};
response: {
service: IService;
};
}
export interface IRequest_Any_Cloudly_DeleteService
extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IRequest_Any_Cloudly_DeleteService
> {
method: 'deleteService';
request: {
identity: IIdentity;
serviceId: string;
};
response: {
success: boolean;
};
}

@ -1,4 +1,5 @@
{
"order": 0,
"name": "@serve.zone/interfaces",
"dependencies": [
"@api.global/typedrequest-interfaces",

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/cloudly',
version: '4.1.2',
version: '4.5.4',
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
}

@ -15,7 +15,7 @@ export const loginAction = loginStatePart.createAction<{ username: string; passw
async (statePartArg, payloadArg) => {
const currentState = statePartArg.getState();
const trLogin =
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secret.IReq_Admin_LoginWithUsernameAndPassword>(
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.admin.IReq_Admin_LoginWithUsernameAndPassword>(
'/typedrequest',
'adminLoginWithUsernameAndPassword'
);
@ -77,20 +77,34 @@ export const dataState = await appstate.getStatePart<IDataState>(
);
// Getting data
export const getAllDataAction = dataState.createAction(async (statePartArg, partialArg?: 'secrets' | 'images') => {
export const getAllDataAction = dataState.createAction(async (statePartArg) => {
let currentState = statePartArg.getState();
// Secrets
const trGetSecrets =
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secret.IReq_Admin_GetConfigBundlesAndSecretGroups>(
// SecretsGroups
const trGetSecretGroups =
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secretgroup.IReq_GetSecretGroups>(
'/typedrequest',
'adminGetConfigBundlesAndSecretGroups'
'getSecretGroups'
);
const response = await trGetSecrets.fire({
const response = await trGetSecretGroups.fire({
identity: loginStatePart.getState().identity,
});
currentState = {
...currentState,
...response,
secretGroups: response.secretGroups,
};
// SecretBundles
const trGetSecretBundles =
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secretbundle.IReq_GetSecretBundles>(
'/typedrequest',
'getSecretBundles'
);
const responseSecretBundles = await trGetSecretBundles.fire({
identity: loginStatePart.getState().identity,
});
currentState = {
...currentState,
secretBundles: responseSecretBundles.secretBundles,
};
// images
@ -104,7 +118,7 @@ export const getAllDataAction = dataState.createAction(async (statePartArg, part
});
currentState = {
...currentState,
...responseImages,
images: responseImages.images,
};
// Clusters
@ -119,7 +133,7 @@ export const getAllDataAction = dataState.createAction(async (statePartArg, part
currentState = {
...currentState,
...responseClusters,
clusters: responseClusters.clusters,
}
return currentState;
@ -130,14 +144,13 @@ export const createSecretGroupAction = dataState.createAction(
async (statePartArg, payloadArg: plugins.interfaces.data.ISecretGroup) => {
let currentState = statePartArg.getState();
const trCreateSecretGroup =
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secret.IReq_Admin_CreateConfigBundlesAndSecretGroups>(
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secretgroup.IReq_CreateSecretGroup>(
'/typedrequest',
'adminCreateConfigBundlesAndSecretGroups'
'createSecretGroup'
);
const response = await trCreateSecretGroup.fire({
identity: loginStatePart.getState().identity,
secretBundles: [],
secretGroups: [payloadArg],
secretGroup: payloadArg,
});
currentState = await dataState.dispatchAction(getAllDataAction, null);
return currentState;
@ -149,14 +162,13 @@ export const deleteSecretGroupAction = dataState.createAction(
async (statePartArg, payloadArg: { secretGroupId: string }) => {
let currentState = statePartArg.getState();
const trDeleteSecretGroup =
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secret.IReq_Admin_DeleteConfigBundlesAndSecretGroups>(
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secretgroup.IReq_DeleteSecretGroupById>(
'/typedrequest',
'adminDeleteConfigBundlesAndSecretGroups'
'deleteSecretGroupById'
);
const response = await trDeleteSecretGroup.fire({
identity: loginStatePart.getState().identity,
secretBundleIds: [],
secretGroupIds: [payloadArg.secretGroupId],
secretGroupId: payloadArg.secretGroupId,
});
currentState = await dataState.dispatchAction(getAllDataAction, null);
return currentState;
@ -168,14 +180,13 @@ export const deleteSecretBundleAction = dataState.createAction(
async (statePartArg, payloadArg: { configBundleId: string }) => {
let currentState = statePartArg.getState();
const trDeleteConfigBundle =
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secret.IReq_Admin_DeleteConfigBundlesAndSecretGroups>(
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.secretbundle.IReq_DeleteSecretBundleById>(
'/typedrequest',
'adminDeleteConfigBundlesAndSecretGroups'
'deleteSecretBundleById'
);
const response = await trDeleteConfigBundle.fire({
identity: loginStatePart.getState().identity,
secretBundleIds: [payloadArg.configBundleId],
secretGroupIds: [],
secretBundleId: payloadArg.configBundleId,
});
currentState = await dataState.dispatchAction(getAllDataAction, null);
return currentState;

@ -10,13 +10,13 @@
"baseUrl": ".",
"paths": {
"@serve.zone/api": [
"./ts_apiclient/index.ts"
"./ts_apiclient/index.js"
],
"@serve.zone/cli": [
"./ts_cliclient/index.ts"
"./ts_cliclient/index.js"
],
"@serve.zone/interfaces": [
"./ts_interfaces/index.ts"
"./ts_interfaces/index.js"
]
}
},