Skip to content

Commits on Source 23

.idea
.gradle
.vscode/
target
web/dashboard-admin/node_modules
web/dashboard-admin/build
web/dashboard-admin/dist
envvar/*.env
!*.env.template
\ No newline at end of file
image: docker:latest
services:
- docker:dind
variables:
# When using dind service, you must instruct docker to talk with the
# daemon started inside of the service. The daemon is available with
# a network connection instead of the default /var/run/docker.sock socket.
#
# The 'docker' hostname is the alias of the service container as described at
# https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#accessing-the-services
#
# If you're using GitLab Runner 12.7 or earlier with the Kubernetes executor and Kubernetes 1.6 or earlier,
# the variable must be set to tcp://localhost:2375 because of how the
# Kubernetes executor connects services to the job container
# DOCKER_HOST: tcp://localhost:2375
#
DOCKER_HOST: tcp://docker:2375
#
# This instructs Docker not to start over TLS.
DOCKER_TLS_CERTDIR: ""
stages:
- build
- deploy
#include:
# - 'java/lc-gdn-api-svc/.gitlab-ci.yml'
build_on_push:
interruptible: false
image: docker.leigh-co.com/mbuilder:1.3
stage: build
tags:
- docker
variables:
DOCKER_BUILDKIT: 1 # Enable BuildKit
rules:
- if: $CI_COMMIT_BRANCH == "master" && $CI_PIPELINE_SOURCE == "push"
when: on_success
changes:
- java/**/*
- web/dashboard-admin/**/*
- nginx/**/*
- grafana/**/*
- when: never
script:
- echo "Running on branch '${CI_COMMIT_BRANCH}'"
- IMAGE_TAG=${CI_COMMIT_SHORT_SHA}
- export VITE_API_MODE=${VITE_API_MODE:-mock}
# API Service
- cd $CI_PROJECT_DIR/java
- docker pull docker.leigh-co.com/strata-api-service:latest || true
- docker build -t docker.leigh-co.com/strata-api-service:latest --target strata-api-service --cache-from docker.leigh-co.com/strata-api-service:latest --build-arg BUILDKIT_INLINE_CACHE=1 .
# DB Migration
- docker pull docker.leigh-co.com/strata-db-migration:latest || true
- docker build -t docker.leigh-co.com/strata-db-migration:latest --target strata-db-migration --cache-from docker.leigh-co.com/strata-db-migration:latest --build-arg BUILDKIT_INLINE_CACHE=1 .
# Frontend
- cd $CI_PROJECT_DIR/web/dashboard-admin
- docker pull docker.leigh-co.com/strata-frontend:latest || true
- docker build -t docker.leigh-co.com/strata-frontend:latest --target frontend --build-arg VITE_API_MODE=${VITE_API_MODE:-mock} --cache-from docker.leigh-co.com/strata-frontend:latest --build-arg BUILDKIT_INLINE_CACHE=1 .
# Push images
- docker push docker.leigh-co.com/strata-api-service:latest
- docker push docker.leigh-co.com/strata-db-migration:latest
- docker push docker.leigh-co.com/strata-frontend:latest
- docker image list
deploy:
tags:
- docker
stage: deploy
interruptible: false
image: docker:latest
rules:
- if: $DEPLOY == "1"
- if: $CI_COMMIT_BRANCH == "master" && $CI_PIPELINE_SOURCE == "push"
when: on_success
changes:
- java/**/*
- web/dashboard-admin/**/*
- nginx/**/*
- grafana/**/*
- when: never
script:
- apk add --no-cache curl openssh-client sshpass jq
- export TAG=${COMMIT:-latest}
- export IS_MOCK=${IS_MOCK:-false}
- export DEPLOYMENT_NAME=${DEPLOYMENT_NAME:-demo}
- export PORT=${PORT:-8080}
- echo $TAG
- echo $DEPLOYMENT_NAME
- echo $PORT
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | sh
- sshpass -p $BAD_PW ssh -o StrictHostKeyChecking=no root@ctl0.demo.cloudweaver.ai "mkdir -p gitlab-ci/ && cd gitlab-ci/ && rm -rf raksasa/"
- sshpass -p $BAD_PW scp -o StrictHostKeyChecking=no -r $CI_PROJECT_DIR root@ctl0.demo.cloudweaver.ai:gitlab-ci/
- sshpass -p $BAD_PW scp -o StrictHostKeyChecking=no $CI_PROJECT_DIR/.secure_files/postgres.env root@ctl0.demo.cloudweaver.ai:gitlab-ci/raksasa/envvar/postgres.env
- |
sshpass -p $BAD_PW ssh -o StrictHostKeyChecking=no root@ctl0.demo.cloudweaver.ai "
export DEPLOYMENT_NAME=${DEPLOYMENT_NAME} && \
export IMAGE_TAG=${TAG} && \
export PORT=${PORT} && \
export VITE_API_MODE=${VITE_API_MODE:-mock} && \
cd gitlab-ci/raksasa && \
docker compose pull && \
docker compose build --no-cache && \
docker compose up -d"
build_specific_commit:
tags:
- docker
stage: deploy
interruptible: false
image: docker:latest
variables:
DOCKER_BUILDKIT: 1 # Enable BuildKit
rules:
- if: $BUILD == "1"
script:
- echo "Running on branch 'master'"
- export VITE_USE_MOCK=${IS_MOCK:-false}
- IMAGE_TAG=${COMMIT}
# API Service
- cd $CI_PROJECT_DIR/java
- docker pull docker.leigh-co.com/strata-api-service:latest || true
- docker build -t docker.leigh-co.com/strata-api-service:${IMAGE_TAG} --target strata-api-service --cache-from docker.leigh-co.com/strata-api-service:latest --build-arg BUILDKIT_INLINE_CACHE=1 .
# DB Migration
- docker pull docker.leigh-co.com/strata-db-migration:latest || true
- docker build -t docker.leigh-co.com/strata-db-migration:${IMAGE_TAG} --target strata-db-migration --cache-from docker.leigh-co.com/strata-db-migration:latest --build-arg BUILDKIT_INLINE_CACHE=1 .
# Frontend
- cd $CI_PROJECT_DIR/web/dashboard-admin
- echo "Building with VITE_API_MODE=${VITE_API_MODE:-mock}"
- docker pull docker.leigh-co.com/strata-frontend:latest || true
- docker build -t docker.leigh-co.com/strata-frontend:${IMAGE_TAG} --target frontend --build-arg VITE_API_MODE=${VITE_API_MODE:-mock} --cache-from docker.leigh-co.com/strata-frontend:latest --build-arg BUILDKIT_INLINE_CACHE=1 .
# Push images
- docker push docker.leigh-co.com/strata-api-service:${IMAGE_TAG}
- docker push docker.leigh-co.com/strata-db-migration:${IMAGE_TAG}
- docker push docker.leigh-co.com/strata-frontend:${IMAGE_TAG}
- docker image list
deploy_demo_latest:
rules:
- if: $OI == "1"
extends: deploy
before_script:
- git fetch origin master
- LATEST_COMMIT=$(git rev-parse --short origin/master)
- export COMMIT=${LATEST_COMMIT}
variables:
DEPLOYMENT_NAME: "demo"
PORT: "8080"
DEPLOY: "1"
build_latest_with_mock:
rules:
- if: $KIW == "1"
extends: build_specific_commit
before_script:
- git fetch origin master
- LATEST_COMMIT=$(git rev-parse --short origin/master)
- export COMMIT=${LATEST_COMMIT}
variables:
IS_MOCK: "true"
BUILD: "1"
\ No newline at end of file
Rakasa
\ No newline at end of file
name: strata-${DEPLOYMENT_NAME}
services:
backend:
image: docker.leigh-co.com/strata-api-service:${IMAGE_TAG}
container_name: backend-${DEPLOYMENT_NAME}
networks:
- app-network
env_file:
- ./envvar/postgres.env
depends_on:
migration:
condition: service_completed_successfully
frontend:
image: docker.leigh-co.com/strata-frontend:${IMAGE_TAG}
container_name: frontend-${DEPLOYMENT_NAME}
networks:
- app-network
depends_on:
- backend
environment:
- VITE_API_MODE=${VITE_API_MODE:-tps}
nginx:
build:
context: ./nginx
container_name: nginx-${DEPLOYMENT_NAME}
environment:
- DEPLOYMENT_NAME=${DEPLOYMENT_NAME}
ports:
- "${PORT}:8080"
volumes:
- ./nginx/nginx.conf.template:/etc/nginx/nginx.conf.template:ro
depends_on:
- backend
- frontend
networks:
- app-network
postgres:
image: postgres:16
container_name: postgres-${DEPLOYMENT_NAME}
env_file:
- ./envvar/postgres.env
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
# Todo: fix this because POSTGRES_USER will always use default value since env_file does nothing to gitlab-ci.yml
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-network
mongodb:
image: mongo:7.0
container_name: mongodb-${DEPLOYMENT_NAME}
environment:
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER:-admin}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-password}
- MONGO_INITDB_DATABASE=${MONGO_DATABASE:-stratadb}
volumes:
- mongodb_data:/data/db
ports:
- "27017:27017"
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-network
migration:
image: docker.leigh-co.com/strata-db-migration:${IMAGE_TAG}
container_name: migration-${DEPLOYMENT_NAME}
networks:
- app-network
env_file:
- ./envvar/postgres.env
depends_on:
postgres:
condition: service_healthy
swagger-editor:
image: swaggerapi/swagger-editor
container_name: swagger-editor-${DEPLOYMENT_NAME}
networks:
- app-network
grafana:
build:
context: ./grafana
container_name: grafana-${DEPLOYMENT_NAME}
user: "472"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-VeryBadPassword99}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_DOMAIN=${DOMAIN:-demo.cloudweaver.ai}
- GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
- GF_SERVER_SERVE_FROM_SUB_PATH=true
- GF_SERVER_ENFORCE_DOMAIN=false
- GF_SECURITY_ALLOW_EMBEDDING=true
- GF_WHITE_LABELLING_APP_TITLE="Cloudweaver Strata"
- GF_WHITE_LABELLING_MENU_LOGO="https://demo.cloudweaver.ai/assets/main-logo.png"
- GF_WHITE_LABELLING_LOADING_LOGO="https://demo.cloudweaver.ai/assets/main-logo.png"
- GF_WHITE_LABELLING_HIDE_EDITION=true
- GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=motherduck-duckdb-datasource,grafana-llm-app
depends_on:
- postgres
networks:
- app-network
restart: unless-stopped
networks:
app-network:
name: app-network-${DEPLOYMENT_NAME}
driver: bridge
volumes:
postgres_data:
name: postgres_data_${DEPLOYMENT_NAME}
mongodb_data:
name: mongodb_data_${DEPLOYMENT_NAME}
grafana_data:
name: grafana_data_${DEPLOYMENT_NAME}
\ No newline at end of file
FROM alpine:3.21.2
RUN apk add --update docker tar junit openrc openjdk21 gradle
RUN rc-update add docker boot
VOLUME [/home/gradle/.gradle]
WORKDIR /home/gradle
CMD ["/bin/bash"]
example
docker run -v "/var/run/docker.sock:/var/run/docker.sock:rw" -v /root/massive/java:/home/gradle docker.leigh-co.com/mbuilder:1.0 -p lc-dns-svc dockerfile docker
#!/bin/sh
docker build -t docker.leigh-co.com/mbuilder:1.3 .
# For backend
JDBC_URL_WITHOUT_DATABASE_NAME=jdbc:postgresql://postgres:5432/
DB_USER=postgres
DB_PASSWORD=hehehehehe
DATABASE_NAME=mydb
# For the actual postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=hehehehehe
POSTGRES_DB=mydb
POSTGRES_HOST_AUTH_METHOD: scram-sha-256
POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256
\ No newline at end of file
package client
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Client represents an API client
type Client struct {
BaseURL string
HTTPClient *http.Client
Headers map[string]string
}
// NewClient creates a new API client with the given base URL
func NewClient(baseURL string) *Client {
return &Client{
BaseURL: baseURL,
HTTPClient: &http.Client{
Timeout: time.Second * 30,
},
Headers: make(map[string]string),
}
}
// SetHeader sets a header to be included in all requests
func (c *Client) SetHeader(key, value string) {
c.Headers[key] = value
}
// SetBearerToken sets the Authorization header with a bearer token
func (c *Client) SetBearerToken(token string) {
c.SetHeader("Authorization", fmt.Sprintf("Bearer %s", token))
}
// SetTimeout sets the client timeout
func (c *Client) SetTimeout(timeout time.Duration) {
c.HTTPClient.Timeout = timeout
}
// Request makes an HTTP request to the API
func (c *Client) Request(ctx context.Context, method, path string, body interface{}, result interface{}) (*http.Response, error) {
url := fmt.Sprintf("%s%s", c.BaseURL, path)
var bodyReader io.Reader
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("error marshaling request body: %w", err)
}
bodyReader = bytes.NewBuffer(jsonBody)
}
req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
// Set default Content-Type if sending a body
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
// Add client headers
for key, value := range c.Headers {
req.Header.Set(key, value)
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, fmt.Errorf("error sending request: %w", err)
}
// Check if we need to parse a response
if result != nil {
defer resp.Body.Close()
// Check for error status code
if resp.StatusCode >= 400 {
bodyBytes, _ := io.ReadAll(resp.Body)
return resp, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(bodyBytes))
}
// Parse response body
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
return resp, fmt.Errorf("error decoding response: %w", err)
}
}
return resp, nil
}
// Get makes a GET request
func (c *Client) Get(ctx context.Context, path string, result interface{}) (*http.Response, error) {
return c.Request(ctx, http.MethodGet, path, nil, result)
}
// Post makes a POST request
func (c *Client) Post(ctx context.Context, path string, body interface{}, result interface{}) (*http.Response, error) {
return c.Request(ctx, http.MethodPost, path, body, result)
}
// Put makes a PUT request
func (c *Client) Put(ctx context.Context, path string, body interface{}, result interface{}) (*http.Response, error) {
return c.Request(ctx, http.MethodPut, path, body, result)
}
// Delete makes a DELETE request
func (c *Client) Delete(ctx context.Context, path string, result interface{}) (*http.Response, error) {
return c.Request(ctx, http.MethodDelete, path, nil, result)
}
// Patch makes a PATCH request
func (c *Client) Patch(ctx context.Context, path string, body interface{}, result interface{}) (*http.Response, error) {
return c.Request(ctx, http.MethodPatch, path, body, result)
}
module go-mock-client
go 1.23.6
package main
import (
"context"
"fmt"
"log"
"time"
"go-mock-client/client"
)
func main() {
// Create a new client with base URL
apiClient := client.NewClient("http://localhost:8080")
// Configure the client
apiClient.SetTimeout(10 * time.Second)
// Create context
ctx := context.Background()
// 1. Create a user
userID := createUser(ctx, apiClient)
if userID == "" {
log.Fatal("Failed to create user")
}
// 2. Login with the user
token := loginUser(ctx, apiClient)
if token == "" {
log.Fatal("Failed to login")
}
// Set the auth token for subsequent requests
apiClient.SetBearerToken(token)
// 3. Create an account
accountID := createAccount(ctx, apiClient)
if accountID == "" {
log.Fatal("Failed to create account")
}
// 4. Assign account to user
assignAccountToUser(ctx, apiClient, userID, accountID)
// 5. Get accounts by user ID
getAccountsByUserID(ctx, apiClient, userID)
}
// Create a user
func createUser(ctx context.Context, apiClient *client.Client) string {
// Prepare request body
requestBody := map[string]interface{}{
"username": "testuser",
"password": "password123",
}
// Send request and get response
var response map[string]interface{}
resp, err := apiClient.Post(ctx, "/api/user", requestBody, &response)
if err != nil {
log.Printf("Error creating user: %v", err)
return ""
}
fmt.Printf("Create User Status: %d, Response: %+v\n", resp.StatusCode, response)
// Extract user ID from response
userID, ok := response["id"].(string)
if !ok {
log.Printf("Invalid response format, couldn't get user ID")
return ""
}
return userID
}
// Login user
func loginUser(ctx context.Context, apiClient *client.Client) string {
// Prepare request body
requestBody := map[string]interface{}{
"username": "testuser",
"password": "password123",
}
// Send request and get response
var response map[string]interface{}
resp, err := apiClient.Post(ctx, "/api/user/login", requestBody, &response)
if err != nil {
log.Printf("Error logging in: %v", err)
return ""
}
fmt.Printf("Login Status: %d, Response: %+v\n", resp.StatusCode, response)
// Extract token from response
token, ok := response["token"].(string)
if !ok {
log.Printf("Invalid response format, couldn't get token")
return ""
}
return token
}
// Create an account
func createAccount(ctx context.Context, apiClient *client.Client) string {
// Prepare request body
requestBody := map[string]interface{}{
"name": "Test Account",
}
// Send request and get response
var response map[string]interface{}
resp, err := apiClient.Post(ctx, "/api/account", requestBody, &response)
if err != nil {
log.Printf("Error creating account: %v", err)
return ""
}
fmt.Printf("Create Account Status: %d, Response: %+v\n", resp.StatusCode, response)
// Extract account ID from response
accountID, ok := response["id"].(string)
if !ok {
log.Printf("Invalid response format, couldn't get account ID")
return ""
}
return accountID
}
// Assign account to user
func assignAccountToUser(ctx context.Context, apiClient *client.Client, userID, accountID string) {
// Prepare request body
requestBody := map[string]interface{}{
"user_id": userID,
"account_id": accountID,
}
// Send request
resp, err := apiClient.Post(ctx, "/api/user/assign_account", requestBody, nil)
if err != nil {
log.Printf("Error assigning account: %v", err)
return
}
fmt.Printf("Assign Account Status: %d\n", resp.StatusCode)
}
// Get accounts by user ID
func getAccountsByUserID(ctx context.Context, apiClient *client.Client, userID string) {
// Prepare path with query parameter
path := fmt.Sprintf("/api/user/accounts?user_id=%s", userID)
// Send request and get response
var accounts []map[string]interface{}
resp, err := apiClient.Get(ctx, path, &accounts)
if err != nil {
log.Printf("Error getting accounts: %v", err)
return
}
fmt.Printf("Get Accounts Status: %d, Accounts count: %d\n", resp.StatusCode, len(accounts))
// Print accounts
for i, account := range accounts {
fmt.Printf("Account %d: %+v\n", i+1, account)
}
}
package auth
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
// JWK represents a JSON Web Key
type JWK struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
Alg string `json:"alg"`
N string `json:"n"`
E string `json:"e"`
}
// JWKS represents a JSON Web Key Set
type JWKS struct {
Keys []JWK `json:"keys"`
}
// LoginResponse represents the response after successful login
type LoginResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
AccessTokenExpiresIn int `json:"access_token_expires_in"`
}
// AccountObj represents an account object
type AccountObj struct {
AccountName string `json:"account_name"`
ID int `json:"id"`
}
// ConsoleScopeResp represents console scope response
type ConsoleScopeResp struct {
EnvironmentCreate bool `json:"environment_create"`
EnvironmentRead bool `json:"environment_read"`
Master bool `json:"master"`
User bool `json:"user"`
}
// TokenClaims represents JWT token claims
type TokenClaims struct {
User string `json:"user"`
Account int `json:"account"`
jwt.RegisteredClaims
}
// Global key pair vars - initialize them in your application
var (
PrivateKey *rsa.PrivateKey
PublicKey *rsa.PublicKey
)
// LoadRSAPrivateKeyFromPEM loads RSA private key from PEM format
func LoadRSAPrivateKeyFromPEM(pemStr string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(pemStr))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the private key")
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
// Try PKCS1 format if PKCS8 fails
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, errors.New("failed to parse private key")
}
}
rsaKey, ok := privateKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("not an RSA private key")
}
return rsaKey, nil
}
// LoadRSAPublicKeyFromPEM loads RSA public key from PEM format
func LoadRSAPublicKeyFromPEM(pemStr string) (*rsa.PublicKey, error) {
block, _ := pem.Decode([]byte(pemStr))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the public key")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, errors.New("failed to parse public key")
}
rsaKey, ok := pub.(*rsa.PublicKey)
if !ok {
return nil, errors.New("not an RSA public key")
}
return rsaKey, nil
}
// CreateJWT creates a JWT token for the given user and account
func CreateJWT(user string) (string, error) {
if PrivateKey == nil {
return "", errors.New("private key not initialized")
}
expiryMinutes := 60
claims := TokenClaims{
User: user,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * time.Duration(expiryMinutes))),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
token.Header["kid"] = "test-key-id"
token.Header["typ"] = "JWT"
tokenString, err := token.SignedString(PrivateKey)
if err != nil {
return "", fmt.Errorf("failed to sign token: %v", err)
}
return tokenString, nil
}
// CreateLoginResponse creates a login response with access and refresh tokens
func CreateLoginResponse(user string) (*LoginResponse, error) {
accessToken, err := CreateJWT(user)
if err != nil {
return nil, err
}
// For simplicity, using a fixed refresh token
// In production, generate and store a secure random token
refreshToken := "random-refresh-token"
return &LoginResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
AccessTokenExpiresIn: 3600,
}, nil
}
// PublicKeyToJWK converts RSA public key to JWK format
func PublicKeyToJWK(publicKey *rsa.PublicKey, kid string) JWK {
// Convert modulus to Base64URL
nBytes := publicKey.N.Bytes()
nBase64 := base64.RawURLEncoding.EncodeToString(nBytes)
// Convert exponent to Base64URL
eBytes := make([]byte, 4)
eBytes[0] = 0
eBytes[1] = byte(publicKey.E >> 16)
eBytes[2] = byte(publicKey.E >> 8)
eBytes[3] = byte(publicKey.E)
// Trim leading zeros
i := 0
for i < len(eBytes) && eBytes[i] == 0 {
i++
}
eBase64 := base64.RawURLEncoding.EncodeToString(eBytes[i:])
return JWK{
Kty: "RSA",
Kid: kid,
Use: "sig",
Alg: "RS256",
N: nBase64,
E: eBase64,
}
}
// CreateJWKS creates a JWKS from the public key
func CreateJWKS() (*JWKS, error) {
if PublicKey == nil {
return nil, errors.New("public key not initialized")
}
jwk := PublicKeyToJWK(PublicKey, "test-key-id")
return &JWKS{
Keys: []JWK{jwk},
}, nil
}
// VerifyToken verifies the JWT token
func VerifyToken(tokenString string) (*TokenClaims, error) {
if PublicKey == nil {
return nil, errors.New("public key not initialized")
}
token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, func(token *jwt.Token) (interface{}, error) {
// Validate the signing method
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// Validate kid if necessary
if kid, ok := token.Header["kid"].(string); ok {
if kid != "test-key-id" {
return nil, fmt.Errorf("unknown key ID: %v", kid)
}
}
return PublicKey, nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, errors.New("invalid token")
}
claims, ok := token.Claims.(*TokenClaims)
if !ok {
return nil, errors.New("invalid claims type")
}
return claims, nil
}
package collections
import (
"go.mongodb.org/mongo-driver/mongo"
)
// Global MongoDB collections
var UserCollection *mongo.Collection
var AccountCollection *mongo.Collection
var EnvCollection *mongo.Collection
var VMTicketCollection *mongo.Collection
var OSCollection *mongo.Collection
var VMCollection *mongo.Collection
var StorageCollection *mongo.Collection
var StorageTicketCollection *mongo.Collection
var SubnetCollection *mongo.Collection
var SubnetTicketCollection *mongo.Collection
var StorageTypeCollection *mongo.Collection
var VmTypeCollection *mongo.Collection
var OperatingSystemCollection *mongo.Collection
// InitCollections initializes the MongoDB collections
func InitCollections(db *mongo.Database) {
UserCollection = db.Collection("users")
AccountCollection = db.Collection("accounts")
EnvCollection = db.Collection("environments")
VMTicketCollection = db.Collection("vm_tickets")
OSCollection = db.Collection("os")
VMCollection = db.Collection("vms")
StorageCollection = db.Collection("storage")
StorageTicketCollection = db.Collection("storage_tickets")
SubnetCollection = db.Collection("subnet")
SubnetTicketCollection = db.Collection("subnet_tickets")
StorageTypeCollection = db.Collection("storage_types")
VmTypeCollection = db.Collection("vm_types")
OperatingSystemCollection = db.Collection("operating_systems")
}
module go-mongo-mock-be
go 1.21
require (
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
go.mongodb.org/mongo-driver v1.17.3
)
require (
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.17.0 // indirect
)
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.13.0/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
package main
import (
"bytes"
"io"
"log"
"net/http"
)
// Middleware to log request details
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Log request details
log.Printf("Request Method: %s, URL: %s", r.Method, r.URL)
log.Printf("Headers: %v", r.Header)
// Only log body for POST/PUT/PATCH requests
if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {
// Read the body
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("Error reading body: %v", err)
http.Error(w, "Error reading request body", http.StatusBadRequest)
return
}
// Log the body
log.Printf("Request Body: %s", string(bodyBytes))
// Restore the body for the actual handler
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
// Call the next handler
next.ServeHTTP(w, r)
})
}
package main
import (
"context"
"log"
"net/http"
"time"
"github.com/gorilla/mux"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go-mongo-mock-be/collections"
"go-mongo-mock-be/svc"
)
func main() {
// Connect to MongoDB
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://admin:password@localhost:27017"))
if err != nil {
log.Fatal(err)
}
// Check connection
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
if err := svc.InitJWT(); err != nil {
log.Fatalf("Failed to initialize JWT keys: %v", err)
}
log.Println("Connected to MongoDB")
// Initialize collections
db := client.Database("cloud_mgmt_demo")
collections.InitCollections(db)
// Initialize router
r := mux.NewRouter()
// Add logging middleware
r.Use(loggingMiddleware)
// Define routes
r.HandleFunc("/api/create_user", svc.CreateUser).Methods("POST")
r.HandleFunc("/api/create_account", svc.CreateAccount).Methods("POST")
r.HandleFunc("/api/user/login", svc.LoginHandler).Methods("POST")
r.HandleFunc("/api/user/assign_account", svc.AssignAccountToUser).Methods("POST")
r.HandleFunc("/api/get_accounts_by_userid", svc.GetAccountsByUserID).Methods("POST")
r.HandleFunc("/api/jwks", svc.JWKSHandler).Methods("GET")
r.HandleFunc("/api/get_account_by_id", svc.GetAccountById).Methods("POST")
r.HandleFunc("/api/create_env", svc.CreateEnv).Methods("POST")
r.HandleFunc("/api/list_envs_by_accountid", svc.GetEnvsByAccountID).Methods("POST")
r.HandleFunc("/api/get_env_by_id", svc.GetEnvById).Methods("POST")
r.HandleFunc("/api/create_vm", svc.CreateVM).Methods("POST")
r.HandleFunc("/api/get_vm_by_accountid", svc.GetVMsByAccountID).Methods("POST")
r.HandleFunc("/api/create_storage", svc.CreateStorage).Methods("POST")
r.HandleFunc("/api/get_storage_by_accountid", svc.GetStoragesByAccountID).Methods("POST")
r.HandleFunc("/api/create_subnet", svc.CreateSubnet).Methods("POST")
r.HandleFunc("/api/get_subnet_by_accountid", svc.GetSubnetsByAccountID).Methods("POST")
r.HandleFunc("/api/create_storage_type", svc.CreateStorageType).Methods("POST")
r.HandleFunc("/api/get_storage_type_by_id", svc.GetStorageTypeById).Methods("POST")
r.HandleFunc("/api/get_storage_types", svc.GetStorageTypes).Methods("POST")
r.HandleFunc("/api/create_vm_type", svc.CreateVmType).Methods("POST")
r.HandleFunc("/api/get_vm_type_by_id", svc.GetVmTypeById).Methods("POST")
r.HandleFunc("/api/get_vm_types", svc.GetVmTypes).Methods("POST")
r.HandleFunc("/api/create_os", svc.CreateOS).Methods("POST")
r.HandleFunc("/api/get_os", svc.GetOsList).Methods("POST")
// Start server
log.Println("Server starting on port 8080...")
log.Fatal(http.ListenAndServe(":8080", r))
}
package models
// User represents a user in the system
// Use json:"password,omitempty" instead of json:"-"
type User struct {
ID string `json:"id" bson:"_id"`
Username string `json:"username" bson:"username"`
Password string `json:"password,omitempty" bson:"password"`
Accounts []string `json:"accounts" bson:"accounts"`
}
// Account represents an account in the system
type Account struct {
ID string `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
}
// LoginRequest represents the data needed for login
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
// LoginResponse represents the response after successful login
type LoginResponse struct {
Token string `json:"token"`
}
type AssignAccountRequest struct {
UserID string `json:"user_id"`
AccountID string `json:"account_id"`
}
type GetAccountsByUserIDRequest struct {
UserID string `json:"user_id"`
}
// Env represents an environment in the system
type Env struct {
ID string `json:"id" bson:"_id"`
AccountID string `json:"account_id" bson:"account_id"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Type string `json:"type" bson:"type"`
CostQuota float64 `json:"cost_quota" bson:"cost_quota"`
Status string `json:"status" bson:"status"`
Cost float64 `json:"cost" bson:"cost"`
CreatedAt string `json:"created_at,omitempty" bson:"created_at,omitempty"`
}
// CreateEnvRequest represents the data needed to create a new environment
type CreateEnvRequest struct {
Name string `json:"name,omitempty"`
AccountID string `json:"account_id"`
CostQuota float64 `json:"cost_quota,omitempty"`
}
type GetEnvsByAccountIdRequest struct {
AccountID string `json:"account_id"`
}
type GetEnvByIdRequest struct {
ID string `json:"id"`
}
// ConsoleScopeResponse represents the console scope permissions
type ConsoleScopeResponse struct {
EnvironmentCreate bool `json:"environment_create"`
EnvironmentRead bool `json:"environment_read"`
User bool `json:"user"`
Master bool `json:"master"`
}
// CreateOSRequest represents the data needed to create a new OS
type CreateOSRequest struct {
Name string `json:"name,omitempty"`
}
// CreateStorageRequest represents the data needed to create new storage
type CreateStorageRequest struct {
EnvironmentID string `json:"environment_id,omitempty"`
Size int `json:"size,omitempty"`
TypeID string `json:"type_id,omitempty"`
Name string `json:"name,omitempty"`
}
// CreateStorageResponse represents the response after creating storage
type CreateStorageResponse struct {
StorageTicketID int `json:"storage_ticket_id,omitempty"`
StorageTicketURL string `json:"storage_ticket_url,omitempty"`
}
// CreateSubnetRequest represents the data needed to create a new subnet
type CreateSubnetRequest struct {
EnvironmentID string `json:"environment_id,omitempty"`
Name string `json:"name,omitempty"`
Subnet string `json:"subnet,omitempty"`
}
// CreateSubnetResponse represents the response after creating a subnet
type CreateSubnetResponse struct {
SubnetTicketID int `json:"subnet_ticket_id,omitempty"`
SubnetTicketURL string `json:"subnet_ticket_url,omitempty"`
}
// CreateVMRequest represents the data needed to create a new VM
type CreateVMRequest struct {
EnvironmentID string `json:"environment_id,omitempty"`
VNCPort int `json:"vnc_port,omitempty"`
Ram int `json:"ram,omitempty"`
TypeID string `json:"type_id,omitempty"`
Cores int `json:"cores,omitempty"`
StorageIDs []string `json:"storage_ids,omitempty"`
OSID string `json:"os_id,omitempty"`
Name string `json:"name,omitempty"`
SubnetIDs []string `json:"subnet_ids,omitempty"`
}
// CreateVMResponse represents the response after creating a VM
type CreateVMResponse struct {
VMTicketID int `json:"vm_ticket_id,omitempty"`
VMTicketURL string `json:"vm_ticket_url,omitempty"`
}
// GetVMListByEnvIDRequest represents the request to get VM list by environment ID
type GetVMListByEnvIDRequest struct {
EnvironmentID string `json:"environment_id,omitempty"`
}
// GetVMListByIDRequest represents the request to get VM by ID
type GetVMListByIDRequest struct {
ID string `json:"id,omitempty"`
}
// GetStorageListByEnvIDRequest represents the request to get storage list by environment ID
type GetStorageListByEnvIDRequest struct {
EnvironmentID string `json:"environment_id,omitempty"`
}
// GetStorageListByIDRequest represents the request to get storage by ID
type GetStorageListByIDRequest struct {
ID string `json:"id,omitempty"`
}
// GetSubnetListByEnvIDRequest represents the request to get subnet list by environment ID
type GetSubnetListByEnvIDRequest struct {
EnvironmentID string `json:"environment_id,omitempty"`
}
// GetSubnetListByIDRequest represents the request to get subnet by ID
type GetSubnetListByIDRequest struct {
ID string `json:"id,omitempty"`
}
// OS represents an operating system in the system
type OS struct {
Name string `json:"name,omitempty"`
ID int `json:"id,omitempty"`
PublicID string `json:"public_id,omitempty"`
}
// LoginResponseExtended represents the extended response after successful login
type LoginResponseExtended struct {
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
AccessTokenExpiresIn int `json:"access_token_expires_in,omitempty"`
}
// RefreshTokenRequest represents the request to refresh a token
type RefreshTokenRequest struct {
RefreshToken string `json:"refresh_token,omitempty"`
}
// Storage represents storage in the system
type Storage struct {
ID string `json:"id,omitempty" bson:"id,omitempty"`
EnvironmentID string `json:"environment_id,omitempty" bson:"environment_id,omitempty"`
EnvironmentName string `json:"environment_name,omitempty" bson:"environment_name,omitempty"`
Size int `json:"size,omitempty" bson:"size,omitempty"`
TypeID string `json:"type_id,omitempty" bson:"type_id,omitempty"`
TypeName string `json:"type_name,omitempty" bson:"type_name,omitempty"` // Added this field
VMID int `json:"vmId,omitempty" bson:"vm_id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
CreatedAt string `json:"created_at,omitempty" bson:"created_at,omitempty"`
HostID string `json:"hostId,omitempty" bson:"host_id,omitempty"`
AttachedAt string `json:"attached_at,omitempty" bson:"attached_at,omitempty"`
Device string `json:"device,omitempty" bson:"device,omitempty"`
PublicID string `json:"public_id,omitempty" bson:"public_id,omitempty"`
Status string `json:"status,omitempty" bson:"status,omitempty"`
}
// StorageTicket represents a storage ticket in the system
type StorageTicket struct {
StorageID string `json:"storage_id,omitempty"`
CreatedAt string `json:"created_at,omitempty" bson:"created_at,omitempty"`
TicketID int `json:"ticket_id,omitempty"`
Status string `json:"status,omitempty"`
}
// Subnet represents a subnet in the system
type Subnet struct {
ID string `json:"id,omitempty" bson:"id,omitempty"`
EnvironmentID string `json:"environment_id,omitempty" bson:"environment_id,omitempty"`
EnvironmentName string `json:"environment_name,omitempty" bson:"environment_name,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Subnet string `json:"subnet,omitempty" bson:"subnet,omitempty"`
CreatedAt string `json:"created_at,omitempty" bson:"created_at,omitempty"`
PublicID string `json:"public_id,omitempty" bson:"public_id,omitempty"`
}
// SubnetTicket represents a subnet ticket in the system
type SubnetTicket struct {
SubnetID string `json:"subnet_id,omitempty"`
CreatedAt string `json:"created_at,omitempty" bson:"created_at,omitempty"`
TicketID int `json:"ticket_id,omitempty"`
Status string `json:"status,omitempty"`
}
// VM represents a virtual machine in the system
type VM struct {
ID string `json:"id,omitempty" bson:"id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
EnvironmentID string `json:"environment_id,omitempty" bson:"environment_id,omitempty"`
EnvironmentName string `json:"environment_name,omitempty"`
TypeID string `json:"type_id,omitempty" bson:"type_id,omitempty"`
TypeName string `json:"type_name,omitempty"`
Status string `json:"status,omitempty" bson:"status,omitempty"`
Cores int `json:"cores,omitempty" bson:"cores,omitempty"`
RAM int `json:"ram,omitempty" bson:"ram,omitempty"`
Storage int `json:"storage,omitempty" bson:"storage,omitempty"`
Cost float64 `json:"cost,omitempty" bson:"cost,omitempty"`
VNCPort int `json:"vnc_port,omitempty" bson:"vnc_port,omitempty"`
StorageIDs []string `json:"storage_ids,omitempty" bson:"storage_ids,omitempty"`
OSID string `json:"os_id,omitempty" bson:"os_id,omitempty"`
CreatedAt string `json:"created_at,omitempty" bson:"created_at,omitempty"`
HostID string `json:"host_id,omitempty" bson:"host_id,omitempty"`
PublicID string `json:"public_id,omitempty" bson:"public_id,omitempty"`
UpdatedAt string `json:"updated_at,omitempty" bson:"updated_at,omitempty"`
SubnetIDs []string `json:"subnet_ids,omitempty" bson:"subnet_ids,omitempty"`
Uptime int `json:"uptime,omitempty" bson:"uptime,omitempty"`
CPUUsage float64 `json:"cpu_usage,omitempty" bson:"cpu_usage,omitempty"`
RAMUsage float64 `json:"ram_usage,omitempty" bson:"ram_usage,omitempty"`
StorageUsage float64 `json:"storage_usage,omitempty" bson:"storage_usage,omitempty"`
}
// VMTicket represents a VM ticket in the system
type VMTicket struct {
VMID string `json:"vm_id,omitempty"`
TicketID int `json:"ticket_id,omitempty"`
Status string `json:"status,omitempty"`
}
type GetAccountByIdRequest struct {
AccountID string `json:"account_id"`
}
type StorageType struct {
ID string `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Cost float64 `json:"cost" bson:"cost"`
}
type CreateStorageType struct {
Name string `json:"name" bson:"name"`
Cost float64 `json:"cost" bson:"cost"`
}
type VmType struct {
ID string `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Cost float64 `json:"cost" bson:"cost"`
}
type CreateVmType struct {
Name string `json:"name" bson:"name"`
Cost float64 `json:"cost" bson:"cost"`
}
type GetStorageTypeByIdRequest struct {
ID string `json:"id"`
}
type GetVmTypeByIdRequest struct {
ID string `json:"id"`
}
type OperatingSystem struct {
ID string `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
}
type CreateOperatingSystem struct {
Name string `json:"name" bson:"name"`
}