From 8bf4ada1f0d55150ec8d41c1e4c05e4013941760 Mon Sep 17 00:00:00 2001 From: KooshaPari Date: Thu, 25 Jun 2026 01:14:02 -0700 Subject: [PATCH] epic/B11: delete local NVMS implementation after repoint Deletes the local NVMS implementation (backend/nvms Go module, backend/nvms.rs Rust parser, backend/odin.nvms config) after B10 repoint to external phenotype-sdk. Updates Taskfile.yml and justfile to remove stale backend/nvms references. --- Taskfile.yml | 8 +- backend/nvms.rs | 281 --------- backend/nvms/.air.toml | 52 -- backend/nvms/.gitignore | 2 - backend/nvms/Builder/.gitignore | 2 - backend/nvms/Builder/go.mod | 7 - backend/nvms/Builder/go.sum | 4 - backend/nvms/Builder/main.go | 18 - backend/nvms/Demonstrator/.gitignore | 2 - backend/nvms/Demonstrator/go.mod | 22 - backend/nvms/Demonstrator/go.sum | 30 - backend/nvms/Demonstrator/main.go | 248 -------- backend/nvms/Provisioner/go.mod | 22 - backend/nvms/Provisioner/go.sum | 30 - backend/nvms/Provisioner/main.go | 214 ------- backend/nvms/examples/analytics.nvms | 67 --- backend/nvms/examples/blog.nvms | 22 - backend/nvms/examples/byteport.nvms | 49 -- backend/nvms/examples/ecommerce.nvms | 43 -- backend/nvms/examples/gameserver.nvms | 61 -- backend/nvms/examples/iot.nvms | 52 -- backend/nvms/examples/ml.nvms | 52 -- backend/nvms/examples/socialnetwork.nvms | 133 ----- backend/nvms/examples/static_portfolio.nvms | 9 - backend/nvms/examples/tradingplatform.nvms | 62 -- backend/nvms/examples/videoplatform.nvms | 55 -- backend/nvms/go.mod | 22 - backend/nvms/go.sum | 34 -- backend/nvms/lib/README.md | 75 --- backend/nvms/lib/auth.go | 260 -------- backend/nvms/lib/aws.go | 556 ------------------ backend/nvms/lib/awspin/api_auth.go | 181 ------ backend/nvms/lib/awspin/ec2/ec2.go | 260 -------- backend/nvms/lib/awspin/ec2/ec2_lib.go | 401 ------------- backend/nvms/lib/awspin/index.go | 8 - backend/nvms/lib/awspin/network/alb.go | 424 ------------- backend/nvms/lib/awspin/network/lib.go | 128 ---- .../nvms/lib/awspin/network/route53/lib.go | 9 - .../lib/awspin/network/route53/route53.go | 167 ------ backend/nvms/lib/awspin/s3/s3.go | 143 ----- backend/nvms/lib/awspin/s3/s3_lib.go | 167 ------ backend/nvms/lib/awspin/types.go | 46 -- backend/nvms/lib/awsutil.go | 325 ---------- backend/nvms/lib/docker.go | 192 ------ backend/nvms/lib/index.go | 56 -- backend/nvms/lib/llm.go | 62 -- .../nvms/lib/providers/anthropic/anthropic.go | 86 --- .../nvms/lib/providers/deepseek/deepseek.go | 86 --- backend/nvms/lib/providers/gemini/gemini.go | 90 --- backend/nvms/lib/providers/index.go | 9 - backend/nvms/lib/providers/lib.go | 18 - backend/nvms/lib/providers/local/local.go | 66 --- backend/nvms/lib/providers/openai/openai.go | 106 ---- backend/nvms/lib/storage.go | 308 ---------- backend/nvms/lib/tunnel.go | 313 ---------- backend/nvms/log.txt | Bin 37952 -> 0 bytes backend/nvms/main.go | 35 -- backend/nvms/models/instances.go | 12 - backend/nvms/models/projects.go | 68 --- backend/nvms/models/repositories.go | 66 --- backend/nvms/models/secrets.go | 9 - backend/nvms/models/types.go | 76 --- backend/nvms/models/users.go | 61 -- backend/nvms/projectManager/deploy.go | 298 ---------- backend/nvms/projectManager/lib.go | 79 --- backend/nvms/projectManager/parser.go | 59 -- backend/nvms/projectManager/terminate.go | 106 ---- backend/nvms/spin | 0 backend/nvms/spin.toml | 57 -- backend/nvms/tmp/build-errors.log | 1 - backend/odin.nvms | 14 - justfile | 4 +- 72 files changed, 6 insertions(+), 7084 deletions(-) delete mode 100644 backend/nvms.rs delete mode 100644 backend/nvms/.air.toml delete mode 100644 backend/nvms/.gitignore delete mode 100644 backend/nvms/Builder/.gitignore delete mode 100644 backend/nvms/Builder/go.mod delete mode 100644 backend/nvms/Builder/go.sum delete mode 100644 backend/nvms/Builder/main.go delete mode 100644 backend/nvms/Demonstrator/.gitignore delete mode 100644 backend/nvms/Demonstrator/go.mod delete mode 100644 backend/nvms/Demonstrator/go.sum delete mode 100644 backend/nvms/Demonstrator/main.go delete mode 100644 backend/nvms/Provisioner/go.mod delete mode 100644 backend/nvms/Provisioner/go.sum delete mode 100644 backend/nvms/Provisioner/main.go delete mode 100644 backend/nvms/examples/analytics.nvms delete mode 100644 backend/nvms/examples/blog.nvms delete mode 100644 backend/nvms/examples/byteport.nvms delete mode 100644 backend/nvms/examples/ecommerce.nvms delete mode 100644 backend/nvms/examples/gameserver.nvms delete mode 100644 backend/nvms/examples/iot.nvms delete mode 100644 backend/nvms/examples/ml.nvms delete mode 100644 backend/nvms/examples/socialnetwork.nvms delete mode 100644 backend/nvms/examples/static_portfolio.nvms delete mode 100644 backend/nvms/examples/tradingplatform.nvms delete mode 100644 backend/nvms/examples/videoplatform.nvms delete mode 100644 backend/nvms/go.mod delete mode 100644 backend/nvms/go.sum delete mode 100644 backend/nvms/lib/README.md delete mode 100644 backend/nvms/lib/auth.go delete mode 100644 backend/nvms/lib/aws.go delete mode 100644 backend/nvms/lib/awspin/api_auth.go delete mode 100644 backend/nvms/lib/awspin/ec2/ec2.go delete mode 100644 backend/nvms/lib/awspin/ec2/ec2_lib.go delete mode 100644 backend/nvms/lib/awspin/index.go delete mode 100644 backend/nvms/lib/awspin/network/alb.go delete mode 100644 backend/nvms/lib/awspin/network/lib.go delete mode 100644 backend/nvms/lib/awspin/network/route53/lib.go delete mode 100644 backend/nvms/lib/awspin/network/route53/route53.go delete mode 100644 backend/nvms/lib/awspin/s3/s3.go delete mode 100644 backend/nvms/lib/awspin/s3/s3_lib.go delete mode 100644 backend/nvms/lib/awspin/types.go delete mode 100644 backend/nvms/lib/awsutil.go delete mode 100644 backend/nvms/lib/docker.go delete mode 100644 backend/nvms/lib/index.go delete mode 100644 backend/nvms/lib/llm.go delete mode 100644 backend/nvms/lib/providers/anthropic/anthropic.go delete mode 100644 backend/nvms/lib/providers/deepseek/deepseek.go delete mode 100644 backend/nvms/lib/providers/gemini/gemini.go delete mode 100644 backend/nvms/lib/providers/index.go delete mode 100644 backend/nvms/lib/providers/lib.go delete mode 100644 backend/nvms/lib/providers/local/local.go delete mode 100644 backend/nvms/lib/providers/openai/openai.go delete mode 100644 backend/nvms/lib/storage.go delete mode 100644 backend/nvms/lib/tunnel.go delete mode 100644 backend/nvms/log.txt delete mode 100644 backend/nvms/main.go delete mode 100644 backend/nvms/models/instances.go delete mode 100644 backend/nvms/models/projects.go delete mode 100644 backend/nvms/models/repositories.go delete mode 100644 backend/nvms/models/secrets.go delete mode 100644 backend/nvms/models/types.go delete mode 100644 backend/nvms/models/users.go delete mode 100644 backend/nvms/projectManager/deploy.go delete mode 100644 backend/nvms/projectManager/lib.go delete mode 100644 backend/nvms/projectManager/parser.go delete mode 100644 backend/nvms/projectManager/terminate.go delete mode 100644 backend/nvms/spin delete mode 100644 backend/nvms/spin.toml delete mode 100644 backend/nvms/tmp/build-errors.log delete mode 100644 backend/odin.nvms diff --git a/Taskfile.yml b/Taskfile.yml index 8e875c299..d791f63a6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,7 +1,7 @@ # Taskfile.yml for BytePort # ------------------------------------------------------------------ # Hybrid task runner for BytePort's three engines: -# - Go (backend/byteport + backend/nvms) # primary backend +# - Go (backend/byteport) # primary backend # - TypeScript / SvelteKit (frontend/web) # primary web frontend # - Rust / Tauri (frontend/web/src-tauri) # desktop/mobile shell # @@ -162,7 +162,7 @@ tasks: dir: . cmds: - | - unformatted=$(gofmt -l backend/byteport backend/nvms 2>/dev/null || true) + unformatted=$(gofmt -l backend/byteport 2>/dev/null || true) if [ -n "$unformatted" ]; then echo "::warning::The following Go files are not gofmt-clean:" echo "$unformatted" @@ -184,7 +184,7 @@ tasks: desc: Auto-format everything in place. cmds: - cargo fmt --all - - cmd: gofmt -w backend/byteport backend/nvms + - cmd: gofmt -w backend/byteport ignore_error: true - cmd: npm run format dir: '{{.FRONTEND_DIR}}' @@ -242,7 +242,7 @@ tasks: - cmd: echo "hygiene active TODO markers" - | grep -rnE "todo!|TODO|FIXME|XXX|HACK|unimplemented!" \ - backend/byteport backend/nvms frontend/web/src frontend/web/src-tauri/src \ + backend/byteport frontend/web/src frontend/web/src-tauri/src \ --include="*.go" --include="*.rs" --include="*.ts" --include="*.svelte" \ 2>/dev/null | grep -v node_modules | grep -v target | head -20 || true - cmd: echo "hygiene governance file presence" diff --git a/backend/nvms.rs b/backend/nvms.rs deleted file mode 100644 index f2cd81e30..000000000 --- a/backend/nvms.rs +++ /dev/null @@ -1,281 +0,0 @@ -/*** - * YAML NVMS FORMAT - * NVMS Acts as a AWS Parse layer that uses resources from your git repo and information in this config to deploy reproducible apps of any size in efficient microvms - * For a typical config we have the following syntax' - * To start off we need the base image, you should always use a :minimal variant to get the full benefits of the microvm - * Then You'll name the whole project, this is the name of the project that will be used in the AWS Console - * Next we'll define services, these can be split by the actual services of your project (e.g. frontend, backend, etc), with different configs for each to allow concurrent deployment - * Each one requires a path to the service, a build script / command, a port, and environment variables (optional) - * We also have present scalability rules, (Min, MAx, CPU Threshold, Memory Threshold) that will allow you to set a range of resources and a threshold to increment(decr = thresh/2) - * If you have a distributed system you'll need to additionally configur a cluster, this will allow you to deploy multiple instances of the same service on different microvms - * furtherore we can directly specify our entire aws infra through the SERVICES: section incl MODE and Engine (e.g. ECS, EKS, etc) to allow for a more complex deployment, - * Network options while mostly self contained are also present to allow for more complex networking options - * AWS Service Config - * Type, This is the specvific service that we are using - * Name, This is the name of the service - * Engine, This is the engine that we are using - * Mode: This is the mode that we are using cluster is typically uncommon unless you're building a distributed system - */ -use std::collections::HashMap; -use yaml2::Yaml; -use thiserror::Error; -use std::str::FromStr; -#[derive(Error, Debug)] -pub enum NVMSError { - #[error("YAML parsing error: {0}")] - YamlError(String), - #[error("Missing required field: {0}")] - MissingField(String), - #[error("Invalid value for field {field}: {message}")] - InvalidValue { - field: String, - message: String, - }, -} - -// Core Types -#[derive(Debug, Clone)] -pub struct ResourceSize { - pub size: u32, - pub unit: ResourceUnit, -} - -#[derive(Debug, Clone)] -pub enum ResourceUnit { - GB, - MB, - KB, -} - -#[derive(Debug, Clone)] -pub struct ScaleRange { - pub min: u32, - pub max: u32, -} - -// Main Configuration -#[derive(Debug)] -pub struct NVMS { - pub from: String, - pub name: String, - pub services: Option>, - pub cluster: Option>, - pub aws: Option, - pub network: Option, - pub scale: Option>, - pub monitoring: Option, - pub backup: Option, -} - -// Service Configurations -#[derive(Debug)] -pub struct ServiceConfig { - pub path: String, - pub build: BuildCommand, - pub port: Option, - pub env: Option>, - pub resources: Option, -} - -#[derive(Debug)] -pub enum BuildCommand { - Single(String), - Multiple(Vec), -} - -#[derive(Debug)] -pub struct ClusterConfig { - pub instances: ScaleRange, - pub path: String, - pub build: BuildCommand, - pub scale: Option, - pub resources: Option, -} - -#[derive(Debug)] -pub struct ScaleRules { - pub cpu_threshold: Option, - pub memory_threshold: Option, - pub queue_threshold: Option, - pub connection_count: Option, - pub player_count: Option, - pub message_rate: Option, - pub gpu_utilization: Option, - pub latency_threshold: Option, -} - -// Resource Configuration -#[derive(Debug)] -pub struct ResourceConfig { - pub cpu: u32, - pub memory: ResourceSize, - pub storage: Option, - pub gpu: Option, -} - -// AWS Configuration -#[derive(Debug)] -pub struct AWSConfig { - pub region: String, - pub services: Vec, -} - -#[derive(Debug)] -pub enum AWSService { - RDS(RDSConfig), - ElastiCache(ElastiCacheConfig), - SQS(SQSConfig), - S3(S3Config), - DynamoDB(DynamoDBConfig), - OpenSearch(OpenSearchConfig), - MSK(MSKConfig), - Lambda(LambdaConfig), - SageMaker(SageMakerConfig), -} - -#[derive(Debug)] -pub struct RDSConfig { - pub name: Option, - pub engine: String, - pub mode: ServiceMode, - pub size: Option, - pub replicas: Option, -} - -#[derive(Debug)] -pub enum ServiceMode { - Single, - Cluster, -} - -#[derive(Debug)] -pub struct DynamoDBConfig { - pub tables: Vec, -} - -#[derive(Debug)] -pub struct DynamoDBTable { - pub name: String, - pub rcu: Option, - pub wcu: Option, -} - -// Network Configuration -#[derive(Debug)] -pub struct NetworkConfig { - pub domain: Option, - pub ssl: bool, - pub load_balancer: Option, - pub cdn: Option, - pub security: SecurityConfig, -} - -#[derive(Debug)] -pub struct LoadBalancerConfig { - pub lb_type: LoadBalancerType, - pub ssl: bool, -} - -#[derive(Debug)] -pub enum LoadBalancerType { - ALB, - NLB, -} - -#[derive(Debug)] -pub struct CDNConfig { - pub enabled: bool, - pub cache_policy: Option, -} - -#[derive(Debug)] -pub struct SecurityConfig { - pub vpc: bool, - pub private_subnets: bool, - pub waf: Option, - pub ddos_protection: Option, - pub rules: Option, -} - -#[derive(Debug)] -pub struct SecurityRules { - pub inbound: Option>, - pub outbound: Option>, - pub latency_threshold: Option, -} - -// Monitoring Configuration -#[derive(Debug)] -pub struct MonitoringConfig { - pub metrics: Vec, - pub alerts: Vec, -} - -#[derive(Debug)] -pub struct AlertConfig { - pub alert_type: String, - pub threshold: ConfigValue, - pub window: String, -} - -// Backup Configuration -#[derive(Debug)] -pub struct BackupConfig { - pub enabled: bool, - pub retention: String, - pub schedule: String, -} - -// Scale Configuration -#[derive(Debug)] -pub struct ScaleConfig { - pub min: u32, - pub max: u32, - pub cpu_threshold: Option, - pub memory_threshold: Option, - pub queue_threshold: Option, -} - -// Config Value handling -#[derive(Debug, Clone)] -pub enum ConfigValue { - String(String), - Number(f64), - EnvRef(String), - AWSRef { service: String, resource: String }, -} - -// Reference parsing -impl FromStr for ConfigValue { - type Err = NVMSError; - - fn from_str(s: &str) -> Result { - if s.starts_with("${env:") { - let var = s.trim_start_matches("${env:") - .trim_end_matches("}"); - Ok(ConfigValue::EnvRef(var.to_string())) - } else if s.starts_with("${aws:") { - let parts: Vec<&str> = s.trim_start_matches("${aws:") - .trim_end_matches("}") - .split(':') - .collect(); - if parts.len() != 2 { - return Err(NVMSError::InvalidValue { - field: "aws_ref".to_string(), - message: "Invalid AWS reference format".to_string(), - }); - } - Ok(ConfigValue::AWSRef { - service: parts[0].to_string(), - resource: parts[1].to_string(), - }) - } else { - Ok(ConfigValue::String(s.to_string())) - } - } -} -fn locateNVMS(path: String) -> String { - // Locate nvms.yaml in targetDirectory - - todo!() -} \ No newline at end of file diff --git a/backend/nvms/.air.toml b/backend/nvms/.air.toml deleted file mode 100644 index 8897e7d33..000000000 --- a/backend/nvms/.air.toml +++ /dev/null @@ -1,52 +0,0 @@ -root = "." -testdata_dir = "testdata" -tmp_dir = "tmp" - -[build] - args_bin = [] - bin = "./tmp/main" - cmd = "spin build --up" - delay = 1000 - exclude_dir = ["assets", "tmp", "vendor", "testdata"] - exclude_file = [] - exclude_regex = ["_test.go"] - exclude_unchanged = false - follow_symlink = false - full_bin = "" - include_dir = [] - include_ext = ["go", "tpl", "tmpl", "html"] - include_file = [] - kill_delay = "0s" - log = "build-errors.log" - poll = false - poll_interval = 0 - post_cmd = [] - pre_cmd = [] - rerun = false - rerun_delay = 500 - send_interrupt = false - stop_on_error = false - -[color] - app = "" - build = "yellow" - main = "magenta" - runner = "green" - watcher = "cyan" - -[log] - main_only = false - silent = false - time = false - -[misc] - clean_on_exit = false - -[proxy] - app_port = 0 - enabled = false - proxy_port = 0 - -[screen] - clear_on_rebuild = false - keep_scroll = true diff --git a/backend/nvms/.gitignore b/backend/nvms/.gitignore deleted file mode 100644 index b56501047..000000000 --- a/backend/nvms/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main.wasm -.spin/ diff --git a/backend/nvms/Builder/.gitignore b/backend/nvms/Builder/.gitignore deleted file mode 100644 index b56501047..000000000 --- a/backend/nvms/Builder/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main.wasm -.spin/ diff --git a/backend/nvms/Builder/go.mod b/backend/nvms/Builder/go.mod deleted file mode 100644 index 79eaa9af9..000000000 --- a/backend/nvms/Builder/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/builder - -go 1.20 - -require github.com/fermyon/spin/sdk/go/v2 v2.2.0 - -require github.com/julienschmidt/httprouter v1.3.0 // indirect diff --git a/backend/nvms/Builder/go.sum b/backend/nvms/Builder/go.sum deleted file mode 100644 index c283accd8..000000000 --- a/backend/nvms/Builder/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/fermyon/spin/sdk/go/v2 v2.2.0 h1:zHZdIqjbUwyxiwdygHItnM+vUUNSZ3CX43jbIUemBI4= -github.com/fermyon/spin/sdk/go/v2 v2.2.0/go.mod h1:kfJ+gdf/xIaKrsC6JHCUDYMv2Bzib1ohFIYUzvP+SCw= -github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= diff --git a/backend/nvms/Builder/main.go b/backend/nvms/Builder/main.go deleted file mode 100644 index 5e9d0815c..000000000 --- a/backend/nvms/Builder/main.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - - spinhttp "github.com/fermyon/spin/sdk/go/v2/http" -) - -func init() { - spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) { - /* Receive a Proj-Obj and Zip Ball, provision a docker img for each service via aws image builder, push to ecr, may need to do push to s3 here*/ - w.Header().Set("Content-Type", "text/plain") - fmt.Fprintln(w, "Built Images and Pushed to ECR") - }) -} - -func main() {} diff --git a/backend/nvms/Demonstrator/.gitignore b/backend/nvms/Demonstrator/.gitignore deleted file mode 100644 index b56501047..000000000 --- a/backend/nvms/Demonstrator/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main.wasm -.spin/ diff --git a/backend/nvms/Demonstrator/go.mod b/backend/nvms/Demonstrator/go.mod deleted file mode 100644 index 8be6ebed2..000000000 --- a/backend/nvms/Demonstrator/go.mod +++ /dev/null @@ -1,22 +0,0 @@ -module github.com/demonstrator - -go 1.25.0 - -require ( - aidanwoods.dev/go-paseto v1.6.0 // indirect - aidanwoods.dev/go-result v0.3.1 // indirect - github.com/danieljoos/wincred v1.2.3 // indirect - github.com/godbus/dbus/v5 v5.2.2 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/julienschmidt/httprouter v1.3.0 // indirect - github.com/zalando/go-keyring v0.2.8 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/sys v0.43.0 // indirect -) - -require ( - github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904 - nvms v0.0.0-00010101000000-000000000000 -) - -replace nvms => ../../nvms diff --git a/backend/nvms/Demonstrator/go.sum b/backend/nvms/Demonstrator/go.sum deleted file mode 100644 index 2f6ccc37a..000000000 --- a/backend/nvms/Demonstrator/go.sum +++ /dev/null @@ -1,30 +0,0 @@ -aidanwoods.dev/go-paseto v1.6.0 h1:JA/PFk5lVsB/PakQGqnfmik/1tIHjE6F0UoPPoAO/nU= -aidanwoods.dev/go-paseto v1.6.0/go.mod h1:LdqkL0Z2mLL0kBWzmHVR1cGFniX+zyOweQmbNKYrDxQ= -aidanwoods.dev/go-result v0.3.1 h1:ee98hpohYUVYbI+pa6gUHTyoRerIudgjky/IPSowDXQ= -aidanwoods.dev/go-result v0.3.1/go.mod h1:GKnFg8p/BKulVD3wsfULiPhpPmrTWyiTIbz8EWuUqSk= -github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= -github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904 h1:56PveUp7kE1r7TYnjN+871XOjCxmp1BRjhBEP/a84ow= -github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904/go.mod h1:9GoW1+MR0gN1OEinITtjPOzmu0dur3U6ty3pIH/gN24= -github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= -github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -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/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= -github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/nvms/Demonstrator/main.go b/backend/nvms/Demonstrator/main.go deleted file mode 100644 index 0ef18e886..000000000 --- a/backend/nvms/Demonstrator/main.go +++ /dev/null @@ -1,248 +0,0 @@ -package main - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net/http" - "nvms/lib" - "nvms/models" - "strings" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -func init() { - spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) { - - var user models.User - var project *models.Project - - project, err := getRequestDetails(w, r) - if err != nil { - fmt.Println("err getting dets: ", err) - http.Error(w, "Error Reading Request", http.StatusInternalServerError) - return - } - //fmt.Println("Proj: ",project) - fmt.Println("User: ", project.User.LLMConfig) - user = project.User - // decrypt portDets - decryptedPortEndpoint, err := lib.DecryptSecret(user.Portfolio.RootEndpoint) - if err != nil { - fmt.Println("Error decrypting endpoint:", err) - http.Error(w, "Error decrypting endpoint", http.StatusInternalServerError) - return - } - decryptedPortKey, err := lib.DecryptSecret(user.Portfolio.APIKey) - if err != nil { - fmt.Println("Error decrypting key:", err) - http.Error(w, "Error decrypting key", http.StatusInternalServerError) - return - } - fmt.Println("Dets: " + decryptedPortEndpoint + " ||| " + decryptedPortKey) - templateStruct, err := getTemplate(decryptedPortEndpoint, decryptedPortKey) - // pull Portfolio API Format - if err != nil { - fmt.Println("error getting template: ", err) - http.Error(w, "error getting template", http.StatusInternalServerError) - } - //fmt.Println("Struct : ", templateStruct) - genRequest := generatePrompt(templateStruct, project) - //fmt.Println("Prompt: ", genRequest) - var credsObj models.LLM = user.LLMConfig - if credsObj.Provider != "local" { - decryptedOAI, err := lib.DecryptSecret(credsObj.Providers[credsObj.Provider].APIKey) - if err != nil { - fmt.Println("Error decrypting key:", err) - http.Error(w, "Error decrypting key", http.StatusInternalServerError) - return - } - - provider := credsObj.Providers[credsObj.Provider] - provider.APIKey = decryptedOAI - credsObj.Providers[credsObj.Provider] = provider - } - filledObject, err := requestFilledTemplate(genRequest, templateStruct, credsObj) - // Generate Response (hand info above to ai langchain) - if err != nil { - fmt.Println("Bad Prompt Req: ", err) - http.Error(w, "Bad Prompt Req", http.StatusInternalServerError) - return - } - fmt.Println("Success , OBJ: ", filledObject) - err = sendToPortfolio(filledObject, decryptedPortEndpoint, decryptedPortKey) - if err != nil { - fmt.Println("Error sending to portfolio: ", err) - http.Error(w, "Error sending to portfolio", http.StatusInternalServerError) - return - } - // post to portfolio - // return\ - w.Header().Set("Content-Type", "text/plain") - fmt.Fprintln(w, "Hello Fermyon!") - }) -} - -func sendToPortfolio(object string, endpoint string, key string) error { - // Clean endpoint and properly join path - uri := strings.TrimRight(endpoint, "/") + "/byteport" - var body *bytes.Buffer - // Check if object is already JSON string - var jsonData interface{} - if err := json.Unmarshal([]byte(object), &jsonData); err != nil { - // If not valid JSON, try to marshal it - - Reqbytes, err := json.Marshal(object) - if err != nil { - return fmt.Errorf("error marshaling object: %v", err) - } - body = bytes.NewBuffer(Reqbytes) - err = json.Indent(body, []byte(object), "", "") - if err != nil { - return fmt.Errorf("error formatting JSON: %v", err) - } - fmt.Println("Marshalled: ", body) - } else { - // If already JSON, use as is - body = bytes.NewBuffer([]byte(object)) - } - - req, err := http.NewRequest("POST", uri, body) - if err != nil { - return fmt.Errorf("error building request: %v", err) - } - - // Add necessary headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+key) - - fmt.Printf("Sending request to: %s\n", uri) - resp, err := spinhttp.Send(req) - if err != nil { - return fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - - // Check response status - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body)) - } - - fmt.Println("Successfully sent to portfolio") - return nil -} -func generatePrompt(template string, project *models.Project) string { - base := `Given a project template and project information, generate a filled portfolio project object. - -Input format: -1. Template: A object with empty fields representing the portfolio project structure -2. Project info: Object containing details about the application being deployed - -Rules: -Dummy Data should always be taking data from the given sample api, if inferring always strictly maintain format e.g for date or colors(hex to hex, etc) -1. For complex array fields (), use a single item following these priority rules: - - If example data exists in the sample filled template, copy the first item's structure, for referential or date values if not calculable use the template value, else calculate your own. Date formats must be in same form as shown. - - If no example exists, use appropriate dummy data - - -3. For technical references: - - logo: Use technology's logo if available, otherwise placeholder - - skills: Include main technologies used - - descriptions: Deep description showing off features catering to both impressing users and potential employers - - type: Map project type to standard categories (Website, Mobile App, CLI Tool, etc.) - -4. For dates: - - Use actual project dates when available - - Default to current date for "from" if not specified - -5. For metadata fields (): - - Use provided info if available - - Leave empty if not relevant -6. In General You're writing a portfolio entry, eccentuate the project's strengths and features, and make it appealing to potential employers or clients. -Sample API call (Filled Example with Unfilled template in template{} (do not include template attribute in response)): -%s - -Project Information: - -Name: %s -Description: %s -Platform: %s -Type: %s -AccessURL(Must include for User access to project): %s -Readme: -%s -User Information: -Name: %s - -Expected response: A filled template object matching the required structure. String-Representable Object usable as response body without modification` - prompt := fmt.Sprintf(base, template, project.Name, project.Description, project.Platform, project.Type, project.AccessURL, project.Readme, project.User.Name) - return prompt -} -func getTemplate(endpoint string, key string) (string, error) { - uri := strings.TrimRight(endpoint, "/") + "/byteport" - - req, err := http.NewRequest("GET", uri, nil) - if err != nil { - return "", fmt.Errorf("error building request: %v", err) - } - - req.Header.Set("Authorization", "Bearer "+key) - - resp, err := spinhttp.Send(req) - if err != nil { - return "", fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("error reading response body: %v", err) - } - - // Try to parse as is first - var jsonTest interface{} - if err := json.Unmarshal(body, &jsonTest); err == nil { - // It's already valid JSON - return string(body), nil - } - - // If not valid JSON, try base64 decode - decoded, err := base64.StdEncoding.DecodeString(string(body)) - if err != nil { - // If both attempts fail, return original with error context - return "", fmt.Errorf("response is neither valid JSON nor base64: %v", err) - } - - return string(decoded), nil -} -func getRequestDetails(w http.ResponseWriter, r *http.Request) (*models.Project, error) { - fmt.Println("Getting Template Dets") - var project models.Project - body, err := io.ReadAll(r.Body) - if err != nil { - fmt.Println("Error reading request body: ", err) - http.Error(w, "Error reading request body", http.StatusInternalServerError) - return nil, err - } - defer r.Body.Close() - fmt.Println("Parsing JSON...") - err = json.Unmarshal(body, &project) - if err != nil { - http.Error(w, "Error parsing JSON", http.StatusBadRequest) - return nil, err - } - return &project, nil -} -func requestFilledTemplate(prompt string, strStruct string, config models.LLM) (string, error) { - - response, err := lib.RequestCompletion(prompt, strStruct, config) - if err != nil { - return "", fmt.Errorf("error getting chat completion: %v", err) - } - return response, nil -} -func main() {} diff --git a/backend/nvms/Provisioner/go.mod b/backend/nvms/Provisioner/go.mod deleted file mode 100644 index 40deb1bea..000000000 --- a/backend/nvms/Provisioner/go.mod +++ /dev/null @@ -1,22 +0,0 @@ -module provisioner - -go 1.25.0 - -require ( - github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904 - nvms v0.0.0-00010101000000-000000000000 -) - -require ( - aidanwoods.dev/go-paseto v1.6.0 // indirect - aidanwoods.dev/go-result v0.3.1 // indirect - github.com/danieljoos/wincred v1.2.3 // indirect - github.com/godbus/dbus/v5 v5.2.2 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/julienschmidt/httprouter v1.3.0 // indirect - github.com/zalando/go-keyring v0.2.8 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/sys v0.43.0 // indirect -) - -replace nvms => ../../nvms diff --git a/backend/nvms/Provisioner/go.sum b/backend/nvms/Provisioner/go.sum deleted file mode 100644 index 2f6ccc37a..000000000 --- a/backend/nvms/Provisioner/go.sum +++ /dev/null @@ -1,30 +0,0 @@ -aidanwoods.dev/go-paseto v1.6.0 h1:JA/PFk5lVsB/PakQGqnfmik/1tIHjE6F0UoPPoAO/nU= -aidanwoods.dev/go-paseto v1.6.0/go.mod h1:LdqkL0Z2mLL0kBWzmHVR1cGFniX+zyOweQmbNKYrDxQ= -aidanwoods.dev/go-result v0.3.1 h1:ee98hpohYUVYbI+pa6gUHTyoRerIudgjky/IPSowDXQ= -aidanwoods.dev/go-result v0.3.1/go.mod h1:GKnFg8p/BKulVD3wsfULiPhpPmrTWyiTIbz8EWuUqSk= -github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= -github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904 h1:56PveUp7kE1r7TYnjN+871XOjCxmp1BRjhBEP/a84ow= -github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904/go.mod h1:9GoW1+MR0gN1OEinITtjPOzmu0dur3U6ty3pIH/gN24= -github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= -github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -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/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= -github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/nvms/Provisioner/main.go b/backend/nvms/Provisioner/main.go deleted file mode 100644 index d61926781..000000000 --- a/backend/nvms/Provisioner/main.go +++ /dev/null @@ -1,214 +0,0 @@ -package main - -import ( - "archive/zip" - "bytes" - "compress/flate" - "encoding/json" - "fmt" - "io" - "net/http" - "nvms/lib" - "nvms/models" - "strings" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -func getArchiveURL(archiveURL string) string { - fmt.Println("Archive URL: ", archiveURL) - splitURL := strings.Split(archiveURL, "/{") - if len(splitURL) > 1 { - return splitURL[0] + "/zipball" - } - return archiveURL -} -func getRootDir(fileMap map[string][]byte) (string, error) { - for key := range fileMap { - // Split the key into parts using "/" as the delimiter - parts := strings.Split(key, "/") - if len(parts) > 1 { - // Return the first part (root directory) - return parts[0] + "/", nil - } - } - return "", fmt.Errorf("no valid root directory found") -} -func InitializeZipManually() { - fmt.Println("Manually initializing archive/zip package...") - - zip.RegisterDecompressor(zip.Store, io.NopCloser) - zip.RegisterDecompressor(zip.Deflate, newFlateReader) - fmt.Println("archive/zip package manually initialized.") -} - -func newFlateReader(r io.Reader) io.ReadCloser { - return flate.NewReader(r) -} - -func processZip(zipResp *http.Response, w http.ResponseWriter, r *http.Request) (string, string, []byte, []string, error) { - - fmt.Println("Processing zip file...") - var readMeContent []byte - var nvmsContent []byte - zipBytes, err := io.ReadAll(zipResp.Body) - if err != nil { - http.Error(w, fmt.Sprintf("Failed to read zip file: %v", err), http.StatusInternalServerError) - } - if len(zipBytes) == 0 { - http.Error(w, "Received empty zip file", http.StatusInternalServerError) - } - zipReader, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes))) - if err != nil { - http.Error(w, fmt.Sprintf("Failed to read zip archive: %v", err), http.StatusInternalServerError) - } - var filePaths []string - for _, file := range zipReader.File { - f, err := file.Open() - if err != nil { - return "", "", nil, nil, fmt.Errorf("failed to open file %s: %w", file.Name, err) - } - - content, err := io.ReadAll(f) - if err != nil { - f.Close() - return "", "", nil, nil, fmt.Errorf("failed to read file %s: %w", file.Name, err) - } - f.Close() - - // Store in file map - filePaths = append(filePaths, file.Name) - - // Check for specific files - lowerName := strings.ToLower(file.Name) - if strings.Contains(lowerName, "readme.md") { - readMeContent = content - } else if strings.Contains(lowerName, "odin.nvms") { - nvmsContent = content - } - } - return string(nvmsContent), string(readMeContent), zipBytes, filePaths, err -} - -func DownloadRepo(w http.ResponseWriter, r *http.Request, archiveURL string, authToken string) (*http.Response, error) { - - // Use Spin's HTTP client for the initial request - req, err := http.NewRequest("GET", archiveURL, nil) - if err != nil { - http.Error(w, fmt.Sprintf("Failed to create request: %v", err), http.StatusInternalServerError) - - } - req.Header.Add("User-Agent", "Byteport") // Set user agent - //req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authToken)) // Add token to headers - req.Header.Add("Accept", "application/vnd.github+json") // Set accept header to GitHub API - //fmt.Println("HEAD: ", req.Header) - // print out full request - //fmt.Println("Request: ", req) - resp, err := spinhttp.Send(req) - if err != nil { - http.Error(w, fmt.Sprintf("Failed to get archive: %v", err), http.StatusInternalServerError) - - } - defer resp.Body.Close() - fmt.Println("Checking for Redirect...") - fmt.Println("Status: ", resp.StatusCode) - - var zipResp *http.Response - if resp.StatusCode != http.StatusFound && resp.StatusCode != http.StatusOK { - http.Error(w, fmt.Sprintf("Expected redirect or file, got: %s", resp.Status), resp.StatusCode) - - } - if resp.StatusCode == http.StatusFound { - fmt.Println("Found, Redirecting...") - // Get redirect URL from Location header - redirectURL := resp.Header.Get("Location") - if redirectURL == "" { - http.Error(w, "No redirect URL provided", http.StatusInternalServerError) - - } - - fmt.Println("Redirect URL: ", redirectURL) - redirReq, err := http.NewRequest("GET", redirectURL, nil) - zipResp, err := spinhttp.Send(redirReq) - if err != nil { - http.Error(w, fmt.Sprintf("Failed to download zip: %v", err), http.StatusInternalServerError) - - } - defer zipResp.Body.Close() - - if zipResp.StatusCode != http.StatusOK { - http.Error(w, fmt.Sprintf("Failed to download zip, status: %s", zipResp.Status), zipResp.StatusCode) - - } - } else { - zipResp = resp - } - return zipResp, nil -} - -func init() { - spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) { - fmt.Println("Received request") - InitializeZipManually() - var project models.Project - fmt.Println("Reading request body...") - body, err := io.ReadAll(r.Body) - if err != nil { - fmt.Println("Error reading request body: ", err) - http.Error(w, "Error reading request body", http.StatusInternalServerError) - return - } - defer r.Body.Close() - fmt.Println("Parsing JSON...") - err = json.Unmarshal(body, &project) - if err != nil { - http.Error(w, "Error parsing JSON", http.StatusBadRequest) - return - } - fmt.Println("Decoding user access token...") - authToken, err := lib.DecryptSecret(project.User.Git.Token) - if err != nil { - fmt.Println("Failed to decrypt user access token: ", err) - http.Error(w, "Failed to decrypt user access token: "+err.Error(), http.StatusInternalServerError) - return - } - fmt.Println("Getting archive URL...") - archiveURL := getArchiveURL(project.Repository.ArchiveURL) - fmt.Println("Initial Archive URL: ", archiveURL) - - zipResp, err := DownloadRepo(w, r, archiveURL, authToken) - if err != nil { - fmt.Println("Error downloading zip file: ", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - fmt.Println("Processing zip file...") - - nvmsFileContent, readMeContent, projectZip, fileMap, err := processZip(zipResp, w, r) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - fmt.Println("Extracted files") - - if nvmsFileContent == "" || readMeContent == "" || projectZip == nil || fileMap == nil { - http.Error(w, "Failed to extract files", http.StatusInternalServerError) - return - } - - response := map[string]interface{}{ - "NVMSFile": nvmsFileContent, - "Readme": readMeContent, - "ZipBall": projectZip, - "FileMap": fileMap, - } - - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(response); err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) - return - } - }) -} - -func main() {} diff --git a/backend/nvms/examples/analytics.nvms b/backend/nvms/examples/analytics.nvms deleted file mode 100644 index 94c84a332..000000000 --- a/backend/nvms/examples/analytics.nvms +++ /dev/null @@ -1,67 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: analytics - -CLUSTER: - ingestion: - INSTANCES: 5-20 - PATH: ./ingestion - BUILD: cargo build --release - SCALE: - QUEUE_THRESHOLD: 1000 - - processor: - INSTANCES: 3-15 - PATH: ./processor - BUILD: cargo build --release - SCALE: - CPU_THRESHOLD: 75 - - aggregator: - INSTANCES: 2-10 - PATH: ./aggregator - BUILD: cargo build --release - -SERVICES: - dashboard: - PATH: ./dashboard - BUILD: npm run build - PORT: 3000 - - api: - PATH: ./api - BUILD: cargo build --release - PORT: 8080 - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: Kinesis - NAME: data-stream - - TYPE: RDS - ENGINE: postgres - MODE: CLUSTER - - TYPE: ElastiCache - ENGINE: redis - MODE: CLUSTER - SIZE: cache.r6g.xlarge - - TYPE: OpenSearch - MODE: CLUSTER - SIZE: r6g.xlarge.search - - TYPE: SQS - NAME: events - - TYPE: S3 - NAME: analytics-data - -NETWORK: - DOMAIN: analytics.example.com - SSL: true - LOAD_BALANCER: - TYPE: ALB - SSL: true - SECURITY: - VPC: true - PRIVATE_SUBNETS: true - RULES: - INBOUND: - - "TCP 443 ANY" - - "TCP 8080 VPC" \ No newline at end of file diff --git a/backend/nvms/examples/blog.nvms b/backend/nvms/examples/blog.nvms deleted file mode 100644 index 57dfb4727..000000000 --- a/backend/nvms/examples/blog.nvms +++ /dev/null @@ -1,22 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: blog - -SERVICES: - frontend: - PATH: ./frontend - BUILD: npm run build - PORT: 3000 - - backend: - PATH: ./backend - BUILD: npm run build - PORT: 8080 - ENV: - - DATABASE_URL=${aws:rds:url} - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: RDS - ENGINE: postgres - SIZE: db.t3.micro \ No newline at end of file diff --git a/backend/nvms/examples/byteport.nvms b/backend/nvms/examples/byteport.nvms deleted file mode 100644 index ce9087328..000000000 --- a/backend/nvms/examples/byteport.nvms +++ /dev/null @@ -1,49 +0,0 @@ -### HEADER -FROM: aws-ubuntu:minimal -NAME: byteport -DESCRIPTION: "BytePort is a Deployment Management System for SWE Professionals" -VERSION: "0.1.2" - -### DEFINITIONS -TEMPLATES: - high: - CPU: 8 - MEMORY: 8 - GPU: false - med: - CPU: 4 - MEMORY: 4 - GPU: false - low: - CPU: 2 - MEMORY: 2 - GPU: false - -SERVICES: - backend: - PATH: ./backend/byteport - BUILD: go run main.go - PORT: 8081 - ENV: - - ENCRYPTION_KEY=t1rSGxkBSy38s8+JNLvHkvJAax4zO/KRsli27VSu6Ks= - - DB_PATH=/data/byteport.db - - RESOURCES: ${template:low} - VOLUMES: - - /data - - frontend: - PATH: ./frontend - BUILD: npm run dev - PORT: 8081 - RESOURCES: ${template:low} - -NETWORK: - PORT_MAPPINGS: - - "8081:8081" - SECURITY: - INBOUND: - - "TCP 8081 ANY" - -AWS: - REGION: us-east-1 diff --git a/backend/nvms/examples/ecommerce.nvms b/backend/nvms/examples/ecommerce.nvms deleted file mode 100644 index 6ec2cebe4..000000000 --- a/backend/nvms/examples/ecommerce.nvms +++ /dev/null @@ -1,43 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: ecommerce - -SERVICES: - frontend: - PATH: ./frontend - BUILD: npm run build - PORT: 3000 - ENV: - - API_URL=${aws:alb:url} - - STRIPE_PUBLIC_KEY=${env:STRIPE_PUBLIC} - - api: - PATH: ./api - BUILD: cargo build --release - PORT: 8080 - ENV: - - DATABASE_URL=${aws:rds:url} - - REDIS_URL=${aws:elasticache:url} - - STRIPE_SECRET_KEY=${env:STRIPE_SECRET} - - worker: - PATH: ./worker - BUILD: cargo build --release - ENV: - - QUEUE_URL=${aws:sqs:url} - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: RDS - ENGINE: postgres - SIZE: db.t3.small - - TYPE: ElastiCache - ENGINE: redis - - TYPE: SQS - NAME: orders - -SCALE: - api: - MIN: 2 - MAX: 5 - CPU_THRESHOLD: 70 \ No newline at end of file diff --git a/backend/nvms/examples/gameserver.nvms b/backend/nvms/examples/gameserver.nvms deleted file mode 100644 index a6c5e8572..000000000 --- a/backend/nvms/examples/gameserver.nvms +++ /dev/null @@ -1,61 +0,0 @@ -FROM: aws-ubuntu:game -NAME: game-server - -CLUSTER: - game-servers: - INSTANCES: 5-50 - PATH: ./game-server - BUILD: cargo build --release - RESOURCES: - CPU: 4 - MEMORY: 8GB - SCALE: - PLAYER_COUNT: 100 - - matchmaker: - INSTANCES: 2-10 - PATH: ./matchmaker - BUILD: cargo build --release - SCALE: - QUEUE_LENGTH: 50 - -SERVICES: - lobby: - PATH: ./lobby - BUILD: npm run build - PORT: 3000 - - api: - PATH: ./api - BUILD: cargo build --release - PORT: 8080 - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: ElastiCache - ENGINE: redis - MODE: CLUSTER - - TYPE: DynamoDB - TABLES: - - NAME: players - - NAME: matches - - TYPE: SQS - NAME: game-events - - TYPE: Route53 - ROUTING: - - TYPE: latency - REGIONS: - - us-east-1 - - eu-west-1 - - ap-southeast-1 - -NETWORK: - DOMAIN: game.example.com - SSL: true - LOAD_BALANCER: - TYPE: NLB - SSL: true - SECURITY: - VPC: true - PRIVATE_SUBNETS: true \ No newline at end of file diff --git a/backend/nvms/examples/iot.nvms b/backend/nvms/examples/iot.nvms deleted file mode 100644 index eabefbcb3..000000000 --- a/backend/nvms/examples/iot.nvms +++ /dev/null @@ -1,52 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: iot-platform - -CLUSTER: - device-gateway: - INSTANCES: 3-15 - PATH: ./gateway - BUILD: cargo build --release - SCALE: - CONNECTION_COUNT: 10000 - - data-processor: - INSTANCES: 5-20 - PATH: ./processor - BUILD: cargo build --release - SCALE: - QUEUE_THRESHOLD: 5000 - -SERVICES: - dashboard: - PATH: ./dashboard - BUILD: npm run build - PORT: 3000 - - api: - PATH: ./api - BUILD: cargo build --release - PORT: 8080 - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: IoT - NAME: device-hub - - TYPE: Kinesis - NAME: data-stream - - TYPE: TimeStreamDB - NAME: metrics - - TYPE: OpenSearch - MODE: CLUSTER - - TYPE: S3 - NAME: device-data - -NETWORK: - DOMAIN: iot.example.com - SSL: true - LOAD_BALANCER: - TYPE: NLB - SSL: true - SECURITY: - VPC: true - PRIVATE_SUBNETS: true \ No newline at end of file diff --git a/backend/nvms/examples/ml.nvms b/backend/nvms/examples/ml.nvms deleted file mode 100644 index 9e3051393..000000000 --- a/backend/nvms/examples/ml.nvms +++ /dev/null @@ -1,52 +0,0 @@ -FROM: aws-ubuntu:ml -NAME: ml-platform - -CLUSTER: - training: - INSTANCES: 1-5 - PATH: ./training - BUILD: python setup.py install - RESOURCES: - GPU: true - SCALE: - GPU_UTILIZATION: 80 - - inference: - INSTANCES: 2-10 - PATH: ./inference - BUILD: python setup.py install - SCALE: - CPU_THRESHOLD: 70 - -SERVICES: - frontend: - PATH: ./frontend - BUILD: npm run build - PORT: 3000 - - api: - PATH: ./api - BUILD: cargo build --release - PORT: 8080 - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: SageMaker - INSTANCES: - - ml.p3.2xlarge - - TYPE: S3 - NAME: model-storage - - TYPE: RDS - ENGINE: postgres - MODE: CLUSTER - - TYPE: ElastiCache - ENGINE: redis - MODE: CLUSTER - -NETWORK: - DOMAIN: ml.example.com - SSL: true - SECURITY: - VPC: true - PRIVATE_SUBNETS: true \ No newline at end of file diff --git a/backend/nvms/examples/socialnetwork.nvms b/backend/nvms/examples/socialnetwork.nvms deleted file mode 100644 index 4bebd18d7..000000000 --- a/backend/nvms/examples/socialnetwork.nvms +++ /dev/null @@ -1,133 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: social-network - -CLUSTER: - api-servers: - INSTANCES: 10-50 - PATH: ./api - BUILD: cargo build --release - SCALE: - CPU_THRESHOLD: 70 - MEMORY_THRESHOLD: 80 - - feed-generator: - INSTANCES: 5-20 - PATH: ./feed - BUILD: cargo build --release - SCALE: - QUEUE_THRESHOLD: 1000 - - recommendation: - INSTANCES: 3-15 - PATH: ./recommendation - BUILD: - - python -m pip install -r requirements.txt - - python setup.py install - SCALE: - GPU_UTILIZATION: 80 - - notification: - INSTANCES: 2-10 - PATH: ./notification - BUILD: cargo build --release - SCALE: - QUEUE_THRESHOLD: 500 - -SERVICES: - frontend: - PATH: ./frontend - BUILD: npm run build - PORT: 3000 - - websocket: - PATH: ./websocket - BUILD: cargo build --release - PORT: 8080 - - search: - PATH: ./search - BUILD: cargo build --release - PORT: 8081 - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: Aurora - ENGINE: postgres - MODE: CLUSTER - REPLICAS: 5 - - TYPE: ElastiCache - ENGINE: redis - MODE: CLUSTER - SIZE: cache.r6g.2xlarge - - TYPE: OpenSearch - MODE: CLUSTER - SIZE: r6g.2xlarge.search - - TYPE: MSK - NAME: events - PARTITIONS: 100 - - TYPE: DynamoDB - TABLES: - - NAME: users - RCU: 5000 - WCU: 1000 - - NAME: posts - RCU: 10000 - WCU: 2000 - - TYPE: S3 - NAME: media-storage - - TYPE: CloudFront - ORIGIN: ${aws:s3:media-storage} - - TYPE: SageMaker - INSTANCES: - - ml.g4dn.xlarge - - TYPE: Lambda - FUNCTIONS: - - NAME: image-processor - - NAME: video-processor - - TYPE: SNS - TOPICS: - - NAME: notifications - - TYPE: SQS - NAME: tasks - -NETWORK: - DOMAIN: social.example.com - SSL: true - LOAD_BALANCER: - TYPE: ALB - SSL: true - CDN: - ENABLED: true - CACHE_POLICY: CachingOptimized - SECURITY: - VPC: true - PRIVATE_SUBNETS: true - WAF: true - DDOS_PROTECTION: true - RULES: - INBOUND: - - "TCP 443 ANY" - - "TCP 8080 VPC" - - "TCP 8081 VPC" - OUTBOUND: - - "TCP ANY VPC" - -MONITORING: - METRICS: - - CPU_UTILIZATION - - MEMORY_UTILIZATION - - REQUEST_LATENCY - - ERROR_RATE - ALERTS: - - TYPE: ERROR_RATE - THRESHOLD: 1% - WINDOW: 5m - - TYPE: LATENCY - THRESHOLD: 500ms - WINDOW: 1m - -BACKUP: - ENABLED: true - RETENTION: 30d - SCHEDULE: "0 0 * * *" \ No newline at end of file diff --git a/backend/nvms/examples/static_portfolio.nvms b/backend/nvms/examples/static_portfolio.nvms deleted file mode 100644 index 6b2478527..000000000 --- a/backend/nvms/examples/static_portfolio.nvms +++ /dev/null @@ -1,9 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: portfolio - -SERVICES: - web: - PATH: ./build - BUILD: npm run build - PORT: 80 - diff --git a/backend/nvms/examples/tradingplatform.nvms b/backend/nvms/examples/tradingplatform.nvms deleted file mode 100644 index 032d45314..000000000 --- a/backend/nvms/examples/tradingplatform.nvms +++ /dev/null @@ -1,62 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: trading-platform - -CLUSTER: - order-processor: - INSTANCES: 10-30 - PATH: ./order-processor - BUILD: cargo build --release - SCALE: - LATENCY_THRESHOLD: 10ms - - market-data: - INSTANCES: 5-15 - PATH: ./market-data - BUILD: cargo build --release - SCALE: - MESSAGE_RATE: 10000 - - risk-calculator: - INSTANCES: 3-10 - PATH: ./risk - BUILD: cargo build --release - -SERVICES: - frontend: - PATH: ./frontend - BUILD: npm run build - PORT: 3000 - - api: - PATH: ./api - BUILD: cargo build --release - PORT: 8080 - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: Aurora - ENGINE: postgres - MODE: CLUSTER - - TYPE: ElastiCache - ENGINE: redis - MODE: CLUSTER - SIZE: cache.r6g.2xlarge - - TYPE: MSK - NAME: market-data - - TYPE: MemoryDB - SIZE: db.r6g.2xlarge - - TYPE: S3 - NAME: trading-data - -NETWORK: - DOMAIN: trading.example.com - SSL: true - LOAD_BALANCER: - TYPE: NLB - SSL: true - SECURITY: - VPC: true - PRIVATE_SUBNETS: true - RULES: - LATENCY_THRESHOLD: 1ms \ No newline at end of file diff --git a/backend/nvms/examples/videoplatform.nvms b/backend/nvms/examples/videoplatform.nvms deleted file mode 100644 index 1ac91f88b..000000000 --- a/backend/nvms/examples/videoplatform.nvms +++ /dev/null @@ -1,55 +0,0 @@ -FROM: aws-ubuntu:minimal -NAME: video-platform - -CLUSTER: - api: - INSTANCES: 3-10 - PATH: ./api - BUILD: cargo build --release - SCALE: - CPU_THRESHOLD: 70 - MEMORY_THRESHOLD: 80 - - transcoder: - INSTANCES: 2-8 - PATH: ./transcoder - BUILD: make build - SCALE: - QUEUE_THRESHOLD: 100 - -SERVICES: - frontend: - PATH: ./frontend - BUILD: npm run build - PORT: 3000 - - websocket: - PATH: ./websocket - BUILD: npm run build - PORT: 8080 - -AWS: - REGION: us-east-1 - SERVICES: - - TYPE: RDS - ENGINE: postgres - MODE: CLUSTER - - TYPE: ElastiCache - ENGINE: redis - MODE: CLUSTER - - TYPE: S3 - NAME: video-storage - - TYPE: SQS - NAME: transcoding-queue - - TYPE: CloudFront - ORIGIN: ${aws:s3:video-storage} - -NETWORK: - DOMAIN: stream.example.com - SSL: true - LOAD_BALANCER: - TYPE: ALB - SSL: true - SECURITY: - VPC: true - PRIVATE_SUBNETS: true \ No newline at end of file diff --git a/backend/nvms/go.mod b/backend/nvms/go.mod deleted file mode 100644 index 0b562e6dc..000000000 --- a/backend/nvms/go.mod +++ /dev/null @@ -1,22 +0,0 @@ -module nvms - -go 1.25.0 - -toolchain go1.25.11 - -require ( - aidanwoods.dev/go-paseto v1.6.0 - github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904 - github.com/google/uuid v1.6.0 - github.com/julienschmidt/httprouter v1.3.0 - github.com/zalando/go-keyring v0.2.8 - gopkg.in/yaml.v2 v2.4.0 -) - -require ( - aidanwoods.dev/go-result v0.3.1 // indirect - github.com/danieljoos/wincred v1.2.3 // indirect - github.com/godbus/dbus/v5 v5.2.2 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/sys v0.44.0 // indirect -) diff --git a/backend/nvms/go.sum b/backend/nvms/go.sum deleted file mode 100644 index 73a4ce4c0..000000000 --- a/backend/nvms/go.sum +++ /dev/null @@ -1,34 +0,0 @@ -aidanwoods.dev/go-paseto v1.6.0 h1:JA/PFk5lVsB/PakQGqnfmik/1tIHjE6F0UoPPoAO/nU= -aidanwoods.dev/go-paseto v1.6.0/go.mod h1:LdqkL0Z2mLL0kBWzmHVR1cGFniX+zyOweQmbNKYrDxQ= -aidanwoods.dev/go-result v0.3.1 h1:ee98hpohYUVYbI+pa6gUHTyoRerIudgjky/IPSowDXQ= -aidanwoods.dev/go-result v0.3.1/go.mod h1:GKnFg8p/BKulVD3wsfULiPhpPmrTWyiTIbz8EWuUqSk= -github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= -github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904 h1:56PveUp7kE1r7TYnjN+871XOjCxmp1BRjhBEP/a84ow= -github.com/fermyon/spin-go-sdk v0.0.0-20240918180601-c2d4ef2e0904/go.mod h1:9GoW1+MR0gN1OEinITtjPOzmu0dur3U6ty3pIH/gN24= -github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= -github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -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/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= -github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/nvms/lib/README.md b/backend/nvms/lib/README.md deleted file mode 100644 index 44c3538cf..000000000 --- a/backend/nvms/lib/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# NVMS Library - -Core functionality for NVM Service (NVMS) including AWS infrastructure management, authentication, and LLM provider integration. - -## Features - -### AWS Infrastructure Management - -- **S3 Operations**: Upload deployment packages to S3 buckets -- **EC2 Deployment**: Launch and manage EC2 instances with automatic build scripts -- **Load Balancing**: Create ALBs with target groups and listener rules -- **DNS Management**: Configure Route53 record sets -- **Build Pack Detection**: Automatic detection of runtime (Go, Node.js, Python, etc.) - -### Authentication - -- **Service Tokens**: PASETO v4-based service authentication -- **Secret Encryption**: AES-CFB encryption for sensitive credentials -- **Key Management**: Environment variable and keyring integration - -### LLM Provider Integration - -- **Multi-Provider Support**: OpenAI, Anthropic, Gemini, DeepSeek, and local models -- **Unified Interface**: Single `RequestCompletion` function for all providers - -## Usage - -```go -import "nvms/lib" - -// Deploy to AWS -s3Info, err := lib.PushToS3(zipBall, accessKey, secretKey, projectName) -instances, err := lib.DeployEC2(accessKey, secretKey, s3Info, service, files) - -// Initialize authentication -if err := lib.InitAuthSystem(); err != nil { - log.Fatal(err) -} - -// Generate service token -token, err := lib.GenerateNVMSToken(project) - -// Request LLM completion -response, err := lib.RequestCompletion(prompt, structStr, config) -``` - -## Subpackages - -| Package | Description | -|---------|-------------| -| `awspin` | AWS service abstractions | -| `awspin/ec2` | EC2 instance management | -| `awspin/network` | VPC, ALB, Route53 management | -| `awspin/s3` | S3 bucket operations | -| `providers` | LLM provider implementations | -| `providers/openai` | OpenAI API integration | -| `providers/anthropic` | Anthropic Claude API | -| `providers/gemini` | Google Gemini API | -| `providers/local` | Local LLM support | -| `providers/deepseek` | DeepSeek API | - -## Exports - -| Category | Functions | -|----------|----------| -| AWS | `PushToS3`, `DeployEC2`, `ProvisionNetwork`, `CreateALBListener`, `SetListenerRules`, `RegisterService`, `AddNewRecord`, `AwaitInitialization`, `TerminateS3`, `TerminateEC2`, `TerminateALB`, `TerminateTargetGroup` | -| Build | `DetectBuildPack`, `GetAWSCredentials` | -| Auth | `InitAuthSystem`, `GenerateSymmetricKey`, `GenerateNVMSToken`, `ValidateServiceToken`, `AuthMiddleware`, `EncryptSecret`, `DecryptSecret`, `GetDecodedEncryptionKey` | -| LLM | `RequestCompletion` | - -## Dependencies - -- `github.com/google/uuid` - UUID generation -- `aidanwoods.dev/go-paseto` - PASETO token generation/validation -- `github.com/zalando/go-keyring` - Secure key storage diff --git a/backend/nvms/lib/auth.go b/backend/nvms/lib/auth.go deleted file mode 100644 index 7fcb63e9b..000000000 --- a/backend/nvms/lib/auth.go +++ /dev/null @@ -1,260 +0,0 @@ -package lib - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/base64" - "fmt" - "log" - "net/http" - "nvms/models" - "os" - "strings" - "time" - - "aidanwoods.dev/go-paseto" - - "github.com/zalando/go-keyring" -) - -const ( - keyringUser = "BytePortUser" - serviceKeyService = "NVMService" -) - -var ( - serviceKey paseto.V4SymmetricKey -) - -func GetSymmetricKey() (string, error) { - return keyring.Get(serviceKeyService, keyringUser) -} -func ensureKeyExists(service, user string) error { - key := os.Getenv("SERVICE_KEY") - if key == "" { - return nil // Key already exists - } - - // Generate and store a new key if not present - newKey := GenerateSymmetricKey() - err := os.Setenv("SERVICE_KEY", newKey) - if err != nil { - return err - } - return nil -} - -func InitAuthSystem() error { - err := ensureKeyExists(serviceKeyService, keyringUser) - if err != nil { - return fmt.Errorf("failed to initialize token key: %w", err) - } - - // Initialize service key - err = ensureKeyExists(serviceKeyService, keyringUser) - if err != nil { - return fmt.Errorf("failed to initialize secrets key: %w", err) - } - - log.Println("Auth system initialized with separate keys for tokens and secrets.") - return nil - -} -func GenerateSymmetricKey() string { - key := paseto.NewV4SymmetricKey() - return key.ExportHex() -} - -func GenerateNVMSToken(project models.Project) (string, error) { - token := paseto.NewToken() - token.SetAudience(serviceKeyService) - token.SetExpiration(time.Now().Add(time.Minute * 10)) - token.SetSubject("deployment") - token.SetIssuer("BytePort") - token.SetIssuedAt(time.Now()) - token.SetNotBefore(time.Now()) - token.SetString("user-id", project.User.UUID) - token.SetString("project-id", project.UUID) - keyHex, err := GetSymmetricKey() - if err != nil { - log.Fatal(err) - } - key, err := paseto.V4SymmetricKeyFromHex(keyHex) - if err != nil { - return "", err - } - - encryptedToken := token.V4Encrypt(key, nil) - - return encryptedToken, nil -} - -func ValidateServiceToken(encryptedToken string) (bool, *paseto.Token, error) { - - keyHex, err := GetSymmetricKey() - if err != nil { - return false, nil, err - } - - key, err := paseto.V4SymmetricKeyFromHex(keyHex) - if err != nil { - return false, nil, err - } - - parser := paseto.NewParser() - parser.AddRule(paseto.ForAudience(serviceKeyService)) - parser.AddRule(paseto.NotExpired()) - - token, err := parser.ParseV4Local(key, encryptedToken, nil) - if err != nil { - return false, nil, err - } - - return true, token, nil -} -func AuthMiddleware(w http.ResponseWriter, r *http.Request) error { - // Get key from environment/config during initialization - keyHex := os.Getenv("SERVICE_KEY") - if keyHex == "" { - http.Error(w, "Server configuration error", http.StatusInternalServerError) - return fmt.Errorf("failed to get service key") - } - - key, err := paseto.V4SymmetricKeyFromHex(keyHex) - if err != nil { - http.Error(w, "Server configuration error", http.StatusInternalServerError) - return err // Don't panic, return error response instead - } - serviceKey = key - - // Validate PASETO token - authHeader, err := r.Cookie("Authorization") - if err != nil { - http.Error(w, "Unauthorized - No auth cookie found", http.StatusUnauthorized) - return http.ErrBodyNotAllowed - } - - authToken := authHeader.Value - if authToken == "" { - http.Error(w, "Unauthorized - Empty auth token", http.StatusUnauthorized) - return err - } - - tokenString := strings.TrimPrefix(authToken, "Bearer ") - parser := paseto.NewParser() - - token, err := parser.ParseV4Local(serviceKey, tokenString, nil) - if err != nil { - http.Error(w, "Unauthorized - Invalid token "+err.Error(), http.StatusUnauthorized) - return err - } - - // Extract claims with error checking - projectID, err := token.GetString("project-id") - if err != nil { - http.Error(w, "Invalid token claims", http.StatusBadRequest) - return err - } - - userID, err := token.GetString("user-id") - if err != nil { - http.Error(w, "Invalid token claims", http.StatusBadRequest) - return err - } - - fmt.Printf("Successfully authenticated user %s for project %s\n", userID, projectID) - w.WriteHeader(http.StatusOK) - return nil -} -func EncryptSecret(secret string) (string, error) { - key, err := GetDecodedEncryptionKey() - if err != nil { - log.Fatal(err) - } - // Validate the key length - if len(key) != 16 && len(key) != 24 && len(key) != 32 { - return "", fmt.Errorf("invalid key length: must be 16, 24, or 32 bytes") - } - - // Create a new AES cipher block - block, err := aes.NewCipher([]byte(key)) - if err != nil { - return "", fmt.Errorf("failed to create cipher: %v", err) - } - - // Generate a random IV - iv := make([]byte, aes.BlockSize) - if _, err := rand.Read(iv); err != nil { - return "", fmt.Errorf("failed to generate IV: %v", err) - } - - // Encrypt the secret using CFB - cipherText := make([]byte, len(secret)) - stream := cipher.NewCFBEncrypter(block, iv) - stream.XORKeyStream(cipherText, []byte(secret)) - - // Prepend the IV to the ciphertext and encode - finalCipherText := append(iv, cipherText...) - return base64.StdEncoding.EncodeToString(finalCipherText), nil -} - -func DecryptSecret(cipherText string) (string, error) { - // Validate the key length - key, err := GetDecodedEncryptionKey() - if err != nil { - log.Fatal(err) - } - if len(key) != 16 && len(key) != 24 && len(key) != 32 { - return "", fmt.Errorf("invalid key length: must be 16, 24, or 32 bytes") - } - - // Decode the base64-encoded ciphertext - data, err := base64.StdEncoding.DecodeString(cipherText) - if err != nil { - return "", fmt.Errorf("failed to decode base64: %v", err) - } - - // Separate the IV and the actual ciphertext - if len(data) < aes.BlockSize { - return "", fmt.Errorf("ciphertext too short") - } - iv := data[:aes.BlockSize] - cipherTextData := data[aes.BlockSize:] - - // Create a new AES cipher block - block, err := aes.NewCipher([]byte(key)) - if err != nil { - return "", fmt.Errorf("failed to create cipher: %v", err) - } - - // Decrypt the ciphertext using CFB - plainText := make([]byte, len(cipherTextData)) - stream := cipher.NewCFBDecrypter(block, iv) - stream.XORKeyStream(plainText, cipherTextData) - - return string(plainText), nil -} -func GetDecodedEncryptionKey() ([]byte, error) { - encryptionKey := os.Getenv("ENCRYPTION_KEY") - if encryptionKey == "" { - return nil, fmt.Errorf("encryption key is not set") - } - - // Trim whitespace from the environment variable - trimmedKey := strings.TrimSpace(encryptionKey) - - // Decode the Base64 key - decodedKey, err := base64.StdEncoding.DecodeString(trimmedKey) - if err != nil { - return nil, fmt.Errorf("failed to decode encryption key: %v", err) - } - - // Validate key length (AES requires 16, 24, or 32 bytes) - keyLength := len(decodedKey) - if keyLength != 16 && keyLength != 24 && keyLength != 32 { - return nil, fmt.Errorf("invalid key length: %d bytes (must be 16, 24, or 32)", keyLength) - } - - return decodedKey, nil -} diff --git a/backend/nvms/lib/aws.go b/backend/nvms/lib/aws.go deleted file mode 100644 index 8f0d98c27..000000000 --- a/backend/nvms/lib/aws.go +++ /dev/null @@ -1,556 +0,0 @@ -package lib - -import ( - "context" - "encoding/base64" - "fmt" - "log" - "os" - "path/filepath" - - aws "nvms/lib/awspin" - ec2 "nvms/lib/awspin/ec2" - awsnet "nvms/lib/awspin/network" - r53 "nvms/lib/awspin/network/route53" - "nvms/lib/awspin/s3" - "nvms/models" - "strings" - - "github.com/google/uuid" -) - -// getAWSRegion returns the AWS region from environment variable or defaults to us-east-1 -func getAWSRegion() string { - if region := os.Getenv("AWS_REGION"); region != "" { - return region - } - return "us-east-1" -} - -var AWSEndpointBase string = "https://%s." + getAWSRegion() + ".amazonaws.com" /* "http://localhost.localstack.cloud:4566"*/ -func PushToS3(zipBall []byte, AccessKey string, SecretKey string, ProjectName string) (S3DeploymentInfo, error) { - log.Println("Uploading to S3...") - cfg := aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("s3"), - Region: getAWSRegion(), - Service: "s3", - } - ctx := context.Background() - s3Client, err := s3.NewS3(cfg) - if err != nil { - log.Printf("S3 client creation failed: %v\n", err) - return S3DeploymentInfo{}, err - } - log.Println("S3 client created") - bucketName := strings.ToLower(ProjectName) + "-bytebucket-" + uuid.New().String() - err = s3Client.CreateBucket(ctx, bucketName) - if err != nil { - log.Printf("Bucket creation failed: %v\n", err) - return S3DeploymentInfo{}, err - } - - log.Printf("Bucket created: %s\n", bucketName) - err = s3Client.PutObject(ctx, bucketName, "src.zip", zipBall) - if err != nil { - log.Printf("PutObject failed: %v\n", err) - return S3DeploymentInfo{}, err - } - log.Printf("Uploaded to S3: %s\n", bucketName) - // return uri/bucket name for later use - - info := S3DeploymentInfo{ - BucketName: bucketName, - ObjectKey: "src.zip", - Region: getAWSRegion(), - BucketARN: fmt.Sprintf("arn:aws:s3:::%s", bucketName), - //ObjectURL: fmt.Sprintf("http://localhost:4566/%s/%s", bucketName, "src.zip"), - ObjectURL: fmt.Sprintf("https://%s.s3.amazonaws.com/%s", bucketName, "src.zip"), - ContentHash: aws.GetPayloadHash(zipBall), - } - return info, nil - -} - -func DeployEC2(AccessKey string, SecretKey string, bucket S3DeploymentInfo, service models.Service, fileMap []string) ([]EC2InstanceInfo, error) { - client, err := ec2.NewEC2(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("ec2"), - Region: getAWSRegion(), - Service: "ec2", - }) - if err != nil { - log.Printf("EC2 client creation failed: %v\n", err) - return []EC2InstanceInfo{}, err - } - - buildScript, err := generateBuildScript(bucket, service, AccessKey, SecretKey, fileMap) - if err != nil { - log.Printf("Error generating build script: %v\n", err) - return []EC2InstanceInfo{}, err - } - log.Println("EC2 client created") - params := map[string]string{ - "ImageId": "ami-01816d07b1128cd2d", - //"ImageId": "ami-024f768332f0", - "InstanceType": "t2.micro", - "UserData": buildScript, - "MinCount": "1", - "MaxCount": "1", - } - log.Println("Creating EC2 instance") - resp, err := client.RunInstances(context.Background(), params) - //fmt.Println(resp) - var instances []EC2InstanceInfo - for _, instance := range resp.Instances { - newInstance := EC2InstanceInfo{ - InstanceID: instance.InstanceId, - PrivateIP: instance.PrivateIpAddress, - State: instance.State.Name, - Region: getAWSRegion(), - } - instances = append(instances, newInstance) - } - return instances, nil -} - -func generateBuildScript(s3Info S3DeploymentInfo, service models.Service, accessKey, secretKey string, files []string) (string, error) { - log.Println("Getting Buildpack") - buildpack, err := DetectBuildPack(files, service) - if err != nil { - log.Printf("Error detecting buildpack: %v\n", err) - log.Printf("Warning: No specific buildpack detected, using default behavior") - buildpack = &models.BuildPack{ - Name: "Generic", - Packages: []string{}, - PreBuild: []string{}, - Build: service.Build, - EnvVars: map[string]string{}, - Start: strings.Join(service.Build, " && "), - DetectFiles: []string{}, - RuntimeVersions: map[string]string{}, - } - return "", err - } - //fmt.Println("Got Buildpack: ", buildpack) - heading := `#!/bin/bash -set -e - -# Configure logging -exec 1> >(logger -s -t $(basename $0)) 2>&1 -BUILD_LOG="/var/log/user-data-build.log" -touch $BUILD_LOG -chmod 644 $BUILD_LOG - -log() { - echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a $BUILD_LOG -}` - script := ` - -log "Starting build process for %s application..." - -# Update system -log "Updating system packages..." -dnf update -y - -# Install AWS CLI and required tools -log "Installing required tools..." -dnf install -y unzip tar gzip - -# Install AWS CLI v2 -log "Installing AWS CLI..." -curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -unzip awscliv2.zip -./aws/install -rm -f awscliv2.zip -rm -rf aws/ - -# Configure AWS credentials -log "Configuring AWS credentials..." -mkdir -p /root/.aws -cat > /root/.aws/credentials << EOF -[default] -aws_access_key_id = %s -aws_secret_access_key = %s -region = us-east-1 -EOF - -# Verify AWS configuration -aws configure list - -# Create working directory -log "Creating working directory..." -mkdir -p /app -cd /app - -# Download code from S3 -log "Downloading code from S3..." -aws s3 cp s3://%s/%s src.zip - -# Unzip the code -log "Extracting code..." -unzip src.zip -rm src.zip - -# Find the actual directory -SERVICE_PATH=%s -EXTRACT_DIR=$(ls -d */ | head -n 1) -cd "$EXTRACT_DIR" - -# Navigate to service directory -log "Navigating to service directory: %s" -cd %s -# Install detected runtime packages -log "Installing detected runtime packages..." -dnf install -y %s - -# Set up environment variables -log "Configuring environment..." -%s - -# Run pre-build commands -log "Running pre-build setup..." -%s - -# Run build commands -log "Running build process..." -%s - -# Create systemd service -log "Creating systemd service..." -cat > /etc/systemd/system/%s.service << EOF -[Unit] -Description=%s Service (%s) -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/app/$EXTRACT_DIR/%s -ExecStart=%s -Restart=always -Environment=PORT=%d -%s - -[Install] -WantedBy=multi-user.target -EOF - - -# Start service -log "Starting service..." -systemctl daemon-reload -systemctl enable %s -systemctl start %s - -log "Build and deployment complete!" -` - log.Println("Building script...") - envVarsList := make([]string, 0, len(buildpack.EnvVars)) - for k, v := range buildpack.EnvVars { - envVarsList = append(envVarsList, fmt.Sprintf("export %s=%s", k, v)) - } - environmentVars := strings.Join(envVarsList, "\n") - // Format script with actual values - formattedScript := heading + fmt.Sprintf(script, - buildpack.Name, // %s for application type - accessKey, // %s for AWS access key - secretKey, // %s for AWS secret key - s3Info.BucketName, // %s for bucket name - s3Info.ObjectKey, // %s for object key - filepath.Base(strings.Trim(service.Path, "/")), - service.Path, // %s for service path (logging) - service.Path, // %s for service path (cd) - strings.Join(buildpack.Packages, " "), // %s for packages - environmentVars, // %s for env vars - strings.Join(buildpack.PreBuild, "\n"), // %s for prebuild - strings.Join(buildpack.Build, " && "), // %s for build commands - service.Name, // %s for service name - service.Name, // %s for service name in Description - buildpack.Name, // %s for buildpack name - service.Path, // %s for WorkingDirectory - buildpack.Start, // %s for ExecStart - service.Port, // %d for PORT - strings.Join(func() []string { // %s for systemd env vars - var envs []string - for k, v := range buildpack.EnvVars { - envs = append(envs, fmt.Sprintf("Environment=%s=%s", k, v)) - } - return envs - }(), "\n"), - service.Name, // %s for enable - service.Name, // %s for start - ) - // Debug: log service and buildpack info - log.Printf("Service: %+v\n", service) - log.Printf("Build Pack: %s\n", buildpack) - return base64.StdEncoding.EncodeToString([]byte(formattedScript)), nil -} - -func ProvisionNetwork(AccessKey string, SecretKey string, projectName string) (*awsnet.CreateLoadBalancerResponse, string, string, error) { - albClient, err := awsnet.NewALB(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("elasticloadbalancing"), - Region: "us-east-1", - Service: "elasticloadbalancing", - }) - if err != nil { - log.Printf("ALB client creation failed: %v\n", err) - return nil, "", "", err - } - ec2Client, err := ec2.NewEC2(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("ec2"), - Region: "us-east-1", - Service: "ec2"}) - if err != nil { - log.Printf("EC2 client creation failed: %v\n", err) - return nil, "", "", err - } - subnet1, subnet2, sgId, vpcId, err := ec2Client.GetAlbNetworkInfo(context.Background()) - if err != nil { - log.Printf("GetAlbNetworkInfo failed: %v\n", err) - return nil, "", "", err - } - /*targetArn, err := albClient.CreateTargetGroup(context.Background(), base+"-"+projectName+"-Byteport", vpcId) - if err != nil { - log.Printf("CreateTargetGroup failed: %v\n", err) - return "","",err - }*/ - //fmt.Println("VPC: ", vpcId); - albInstance, err := albClient.CreateInternetApplicationLoadbalancer(context.Background(), projectName, sgId, subnet1, subnet2) - if err != nil { - log.Printf("CreateInternetApplicationLoadbalancer failed: %v\n", err) - return nil, "", "", err - } - //loadBalancerArn := albInstance.CreateLoadBalancerResult.LoadBalancers.Member.LoadBalancerArn - publicDNS := albInstance.CreateLoadBalancerResult.LoadBalancers.Member.DNSName - - // for each service create targetgroup service-TG -> ALB Listener Rule Path /service/* -> service-TG - - log.Printf("ALB created with DNS: %s\n", publicDNS) - - return albInstance, vpcId, publicDNS, nil - -} -func CreateALBListener(AccessKey string, SecretKey string, projectName string, loadBalancerArn string, vpcId string, instanceId string, port int) (string, string, error) { - albClient, err := awsnet.NewALB(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("elasticloadbalancing"), - Region: "us-east-1", - Service: "elasticloadbalancing", - }) - targetArn, err := RegisterService(AccessKey, SecretKey, loadBalancerArn, projectName, "main", vpcId, instanceId, port) - listenerResponse, err := albClient.CreateListener(context.Background(), projectName, loadBalancerArn, targetArn) - if err != nil { - log.Printf("CreateListener failed: %v\n", err) - return "", "", err - } - listenerArn := listenerResponse.CreateListenerResult.Listeners.Member.ListenerArn - log.Printf("ALB Listener created: %s\n", listenerArn) - return listenerArn, targetArn, nil - -} -func SetListenerRules(AccessKey string, SecretKey string, ListenerArn string, TargetArn string, serviceName string, priority int) error { - c, err := awsnet.NewALB(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("elasticloadbalancing"), - Region: "us-east-1", - Service: "elasticloadbalancing", - }) - if err != nil { - log.Printf("ALB client creation failed: %v\n", err) - return err - } - err = c.CreateListenerRule(context.Background(), ListenerArn, TargetArn, serviceName, priority) - if err != nil { - log.Printf("CreateListenerRule failed: %v\n", err) - return err - } - return nil -} -func RegisterService(AccessKey string, SecretKey string, loadBalancerArn string, projectName string, serviceName string, vpcId string, instanceId string, port int) (string, error) { - albClient, err := awsnet.NewALB(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("elasticloadbalancing"), - Region: "us-east-1", - Service: "elasticloadbalancing", - }) - if err != nil { - log.Printf("ALB client creation failed: %v\n", err) - return "", err - } - targetArn, err := albClient.CreateTargetGroup(context.Background(), serviceName+"-"+projectName+"-Byteport", vpcId) - if err != nil { - log.Printf("CreateTargetGroup failed: %v\n", err) - return "", err - } - err = albClient.RegisterTarget(context.Background(), targetArn, instanceId, port) - if err != nil { - log.Printf("RegisterTarget failed: %v\n", err) - return "", err - } - log.Printf("Service registered: %s\n", targetArn) -} -func AddNewRecord(AccessKey string, SecretKey string, domainName string, zoneID string, projectName string, value string) (string, error) { - c, err := r53.NewRoute53(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("route53"), - Region: "us-east-1", - Service: "route53", - }) - if err != nil { - log.Printf("Route53 client creation failed: %v\n", err) - return "", err - } - - err = c.CreateRecordSet(context.Background(), zoneID, domainName, "A", value, 300, projectName) - if err != nil { - log.Printf("CreateRecordSet failed: %v\n", err) - return "", err - } - log.Println("Record set created successfully") - return "Success", nil - -} -func AwaitInitialization(AccessKey string, SecretKey string, instanceIDs []string) error { - log.Println("Waiting for instances to initialize...") - c, err := ec2.NewEC2(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("ec2"), - Region: "us-east-1", - Service: "ec2", - }) - if err != nil { - log.Printf("EC2 client creation failed: %v\n", err) - return err - } - log.Println("EC2 client created") - err = c.WaitForEC2Running(instanceIDs, context.Background()) - if err != nil { - log.Printf("WaitForEC2Running failed: %v\n", err) - return err - } - log.Println("Instances initialized") - return nil -} - -func TerminateS3(resource models.AWSResource, AccessKey string, SecretKey string) error { - c, err := s3.NewS3(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("s3"), - Region: "us-east-1", - Service: "s3", - }) - if err != nil { - fmt.Println(err) - return err - } - - err = c.DeleteBucket(context.Background(), resource.ID) - if err != nil { - err = c.DeleteObject(context.Background(), resource.ID, "src.zip") - if err != nil { - fmt.Println(err) - return err - } - err = c.DeleteBucket(context.Background(), resource.ID) - if err != nil { - fmt.Println(err) - return err - } - return err - } - fmt.Println("Record set created successfully.") - return nil -} - -func TerminateEC2(resource models.AWSResource, AccessKey string, SecretKey string) error { - c, err := ec2.NewEC2(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - Endpoint: getServiceEndpoint("ec2"), - Region: "us-east-1", - Service: "ec2", - }) - if err != nil { - fmt.Println(err) - return err - } - - err = c.TerminateInstances(context.Background(), []string{resource.ID}) - if err != nil { - fmt.Println(err) - return err - } - fmt.Println("Record set created successfully.") - - return nil -} - -func TerminateALB(resource models.AWSResource, AccessKey string, SecretKey string) error { - c, err := awsnet.NewALB(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("elasticloadbalancing"), - Region: "us-east-1", - Service: "elasticloadbalancing", - }) - if err != nil { - fmt.Println(err) - return err - } - - err = c.DeleteLoadbalancer(context.Background(), resource.ID) - if err != nil { - fmt.Println(err) - return err - } - fmt.Println("Record set created successfully.") - return nil -} - -func TerminateTargetGroup(resource models.AWSResource, AccessKey string, SecretKey string) error { - c, err := awsnet.NewALB(aws.Config{ - AccessKeyId: AccessKey, - SecretAccessKey: SecretKey, - SessionToken: "", - Endpoint: getServiceEndpoint("elasticloadbalancing"), - Region: "us-east-1", - Service: "elasticloadbalancing", - }) - if err != nil { - fmt.Println(err) - return err - } - - err = c.DeleteTargetGroup(context.Background(), resource.ID) - if err != nil { - fmt.Println(err) - return err - } - - fmt.Println("Record set created successfully.") - return nil -} diff --git a/backend/nvms/lib/awspin/api_auth.go b/backend/nvms/lib/awspin/api_auth.go deleted file mode 100644 index c10a5e2dd..000000000 --- a/backend/nvms/lib/awspin/api_auth.go +++ /dev/null @@ -1,181 +0,0 @@ -package awspin - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "fmt" - "net/http" - "net/url" - "sort" - "strings" -) - -func GetAuthorizationHeader(config *Config, req *http.Request, date *AwsDate, payloadHash string) string { - canonicalHeaders, signedHeaders := getHeaderStrings(req.Header) - canonicalRequest := getCanonicalRequest(req, signedHeaders, canonicalHeaders, payloadHash) - stringToSign := getStringToSign(config, date, canonicalRequest) - signature := getSignature(config, date, stringToSign) - credential := strings.Join([]string{config.AccessKeyId, date.GetDate(), config.Region, config.Service, "aws4_request"}, "/") - - return fmt.Sprintf("AWS4-HMAC-SHA256 Credential=%s, SignedHeaders=%s, Signature=%s", - credential, signedHeaders, signature) -} -func getHeaderStrings(headers http.Header) (string, string) { - // Formatted as header_key_1:header_value_1\nheader_key_2:header_value_2\n - canonicalHeaders := "" - // Formatted as header_key_1;header_key_2 - signedHeaders := "" - headerKeys := make([]string, 0, len(headers)) - for key := range headers { - headerKeys = append(headerKeys, key) - } - // Header names must appear in alphabetical order - sort.Strings(headerKeys) - - for _, key := range headerKeys { - // Each header name must use lowercase characters - lowerCaseKey := strings.ToLower(key) - canonicalHeaders += lowerCaseKey + ":" + headers.Get(key) + "\n" - if signedHeaders == "" { - signedHeaders += lowerCaseKey - } else { - signedHeaders += ";" + lowerCaseKey - } - } - - return canonicalHeaders, signedHeaders -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#request-string -func getStringToSign(config *Config, date *AwsDate, canonicalRequest string) string { - scope := strings.Join([]string{date.GetDate(), config.Region, config.Service, "aws4_request"}, "/") - return strings.Join([]string{"AWS4-HMAC-SHA256", date.GetTime(), scope, GetPayloadHash([]byte(canonicalRequest))}, "\n") -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#signing-key -func getSignature(config *Config, date *AwsDate, stringToSign string) string { - sign := func(key []byte, data []byte) []byte { - hash := hmac.New(sha256.New, key) - hash.Write(data) - - return hash.Sum(nil) - } - - dateKey := sign([]byte("AWS4"+config.SecretAccessKey), []byte(date.GetDate())) - regionKey := sign(dateKey, []byte(config.Region)) - serviceKey := sign(regionKey, []byte(config.Service)) - signingKey := sign(serviceKey, []byte("aws4_request")) - - return hex.EncodeToString(sign(signingKey, []byte(stringToSign))) -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request -func getCanonicalRequest(req *http.Request, signedHeaders, canonicalHeaders, payloadHash string) string { - escapedUrl := req.URL.EscapedPath() - if !strings.HasPrefix(escapedUrl, "/") { - // The path MUST start with a "/" - escapedUrl = "/" + escapedUrl - } - - return strings.Join([]string{ - req.Method, - escapedUrl, - req.URL.RawQuery, - canonicalHeaders, - signedHeaders, - payloadHash, - }, "\n") -} - -func GetPayloadHash(payload []byte) string { - hash := sha256.New() - hash.Write(payload) - return hex.EncodeToString(hash.Sum(nil)) -} - -// GetCanonicalQueryString sorts and encodes query parameters according to AWS standards -func GetCanonicalQueryString(params map[string]string) string { - // Create list of parameter names - paramNames := make([]string, 0, len(params)) - for name := range params { - paramNames = append(paramNames, name) - } - sort.Strings(paramNames) - - // Build canonical query string - var result strings.Builder - for i, name := range paramNames { - if i > 0 { - result.WriteString("&") - } - result.WriteString(url.QueryEscape(name)) - result.WriteString("=") - result.WriteString(url.QueryEscape(params[name])) - } - return result.String() -} - -// Helper function to calculate SHA256 hash -func GetSHA256Hash(data []byte) string { - hash := sha256.Sum256(data) - return hex.EncodeToString(hash[:]) -} - -// Helper function for HMAC-SHA256 -func HmacSHA256(key, data []byte) []byte { - hash := hmac.New(sha256.New, key) - hash.Write(data) - return hash.Sum(nil) -} - -// GetQueryStringHash calculates the hash of the canonical query string -func GetQueryStringHash(params map[string]string) string { - canonicalQuery := GetCanonicalQueryString(params) - hash := sha256.New() - hash.Write([]byte(canonicalQuery)) - return hex.EncodeToString(hash.Sum(nil)) -} - -// GetCanonicalRequestForQueryAPI builds the canonical request string for Query APIs like EC2 -func GetCanonicalRequestForQueryAPI(method, uri string, params map[string]string, headers http.Header, signedHeadersList []string) string { - canonicalQuery := GetCanonicalQueryString(params) - - // Build canonical headers string - var canonicalHeaders strings.Builder - for _, header := range signedHeadersList { - canonicalHeaders.WriteString(strings.ToLower(header)) - canonicalHeaders.WriteString(":") - canonicalHeaders.WriteString(strings.Join(headers[http.CanonicalHeaderKey(header)], ",")) - canonicalHeaders.WriteString("\n") - } - - // Build canonical request - canonicalRequest := strings.Join([]string{ - method, - uri, - canonicalQuery, - canonicalHeaders.String(), - strings.Join(signedHeadersList, ";"), - GetQueryStringHash(params), - }, "\n") - - return canonicalRequest -} - -// GetCanonicalHeaders builds the canonical headers string for the signature -func GetCanonicalHeaders(headers http.Header, signedHeaders []string) string { - var canonicalHeaders strings.Builder - - for _, header := range signedHeaders { - canonicalHeaders.WriteString(header) - canonicalHeaders.WriteString(":") - // Get header values, trim spaces, and collapse multiple spaces - value := strings.TrimSpace(headers.Get(header)) - value = strings.Join(strings.Fields(value), " ") - canonicalHeaders.WriteString(value) - canonicalHeaders.WriteString("\n") - } - - return canonicalHeaders.String() -} diff --git a/backend/nvms/lib/awspin/ec2/ec2.go b/backend/nvms/lib/awspin/ec2/ec2.go deleted file mode 100644 index 31303663b..000000000 --- a/backend/nvms/lib/awspin/ec2/ec2.go +++ /dev/null @@ -1,260 +0,0 @@ -package ec2 - -import ( - "bytes" - "context" - "encoding/xml" - "fmt" - "io" - aws "nvms/lib/awspin" - "time" -) - -// RunInstances launches new EC2 instances -func (c *Client) RunInstances(ctx context.Context, params map[string]string) (*RunInstancesResponse, error) { - params["Action"] = "RunInstances" - //fmt.Println("Creating instance: ", params) - - req, err := c.newRequest(ctx, "POST", params, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return nil, err - } - - resp, err := c.do(req) - if err != nil { - fmt.Println("Error creating instance: ", err) - return nil, err - } - defer resp.Body.Close() - - var result RunInstancesResponse - if err := xml.NewDecoder(resp.Body).Decode(&result); err != nil { - fmt.Println("Error decoding response: ", err) - return nil, fmt.Errorf("failed to decode response: %w", err) - } - fmt.Println("Instance created") - return &result, nil -} - -// DescribeInstances gets information about EC2 instances -func (c *Client) DescribeInstances(ctx context.Context, instanceIds []string) (*DescribeInstancesResponse, error) { - params := map[string]string{ - "Action": "DescribeInstances", - } - - for i, id := range instanceIds { - params[fmt.Sprintf("InstanceId.%d", i+1)] = id - } - - req, err := c.newRequest(ctx, "GET", params, nil) - if err != nil { - return nil, err - } - - resp, err := c.do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - var result DescribeInstancesResponse - if err := xml.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &result, nil -} - -// TerminateInstances terminates EC2 instances -func (c *Client) TerminateInstances(ctx context.Context, instanceIds []string) error { - params := map[string]string{ - "Action": "TerminateInstances", - } - - for i, id := range instanceIds { - params[fmt.Sprintf("InstanceId.%d", i+1)] = id - } - - req, err := c.newRequest(ctx, "POST", params, nil) - if err != nil { - return err - } - - resp, err := c.do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} -func (c *Client) describeDefaultVPC(ctx context.Context) (string, error) { - /* - curl "https://ec2.us-east-1.amazonaws.com/?Action=DescribeVpcs&Filter.1.Name=isDefault&Filter.1.Value.1=true&Version=2016-11-15" \ - -H "Content-Type: application/x-www-form-urlencoded" \ - --aws-sigv4 "aws:amz:us-east-1:ec2" \ - --user "YOUR_ACCESS_KEY:YOUR_SECRET_KEY" - */ - fmt.Println("getting vpc") - params := map[string]string{ - "Action": "DescribeVpcs", - "Filter.1.Name": "isDefault", - "Filter.1.Value.1": "true", - "Version": "2016-11-15"} - req, err := c.newRequest(ctx, "GET", params, nil) - if err != nil { - fmt.Println("Err Getting VPC Req: ", err) - return "", err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error getting VPC: ", err) - return "", err - } - - defer resp.Body.Close() - var defaultVPC DescribeVpcsResponse - //fmt.Println("Resp: ", resp) - if err := xml.NewDecoder(resp.Body).Decode(&defaultVPC); err != nil { - fmt.Println("Error decoding response: ", err) - return "", fmt.Errorf("failed to decode response: %w", err) - } - return defaultVPC.Vpcs[0].VpcId, nil - -} -func (c *Client) DescribeSubnets(ctx context.Context, vpcId string) (*DescribeSubnetsResponse, error) { - - params := map[string]string{ - "Action": "DescribeSubnets", - "Filter.1.Name": "vpc-id", - "Filter.1.Value.1": vpcId, - - "Version": "2016-11-15", - } - req, err := c.newRequest(ctx, "GET", params, nil) - if err != nil { - return nil, err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error getting Subnet: ", err) - return nil, err - } - defer resp.Body.Close() - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) - } - //fmt.Println("Response Body:", string(bodyBytes)) - - // Reset the response body for decoding - resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - var subnets DescribeSubnetsResponse - if err := xml.NewDecoder(resp.Body).Decode(&subnets); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - return &subnets, nil -} -func (c *Client) DescribeSecurityGroups(ctx context.Context, vpcId string) (*DescribeSecurityGroupsResponse, error) { - /* - curl "https://ec2.us-east-1.amazonaws.com/?Action=DescribeSecurityGroups&Filter.1.Name=vpc-id&Filter.1.Value.1=vpc-xxxxx&Version=2016-11-15" \ - -H "Content-Type: application/x-www-form-urlencoded" \ - --aws-sigv4 "aws:amz:us-east-1:ec2" \ - --user "YOUR_ACCESS_KEY:YOUR_SECRET_KEY"*/ - params := map[string]string{ - "Action": "DescribeSecurityGroups", - "Filter.1.Name": "vpc-id", - "Filter.1.Value.1": vpcId, - // filter by azn e.g. us-east 1 - - "Version": "2016-11-15"} - req, err := c.newRequest(ctx, "GET", params, nil) - if err != nil { - return nil, err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error getting VPC: ", err) - return nil, err - } - defer resp.Body.Close() - - var securityGroups DescribeSecurityGroupsResponse - if err := xml.NewDecoder(resp.Body).Decode(&securityGroups); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - return &securityGroups, nil -} -func (c *Client) GetAlbNetworkInfo(ctx context.Context) (string, string, string, string, error) { - fmt.Println("Getting VPC") - vpcId, err := c.describeDefaultVPC(ctx) - if err != nil { - return "", "", "", "", err - } - fmt.Println("Getting Subnets") - subnets, err := c.DescribeSubnets(ctx, vpcId) - if err != nil { - return "", "", "", "", err - } - fmt.Println("Getting Security Groups") - securityGroups, err := c.DescribeSecurityGroups(ctx, vpcId) - if err != nil { - return "", "", "", "", err - } - fmt.Println("Got ALB NetInfo") - //fmt.Println("Subnets: ", subnets) - //fmt.Println("Security Groups: ", securityGroups) - //fmt.Println("VPC: ", vpcId) - subnet1, subnet2 := subnets.SubnetSet[0].SubnetId, subnets.SubnetSet[1].SubnetId - return subnet1, subnet2, securityGroups.SecurityGroupInfo.Item.GroupId, vpcId, nil -} -func (c *Client) WaitForEC2Running(instanceIDs []string, ctx context.Context) error { - maxAttempts := 60 // Adjust as needed - fmt.Println("Waiting for EC2 to initialize: ", instanceIDs) - - for attempt := 0; attempt < maxAttempts; attempt++ { - fmt.Printf("Attempt %d: Checking EC2 instance statuses\n", attempt+1) - resp, err := c.DescribeInstances(ctx, instanceIDs) - if err != nil { - // Check if error is InvalidInstanceID.NotFound and retry - if awsErr, ok := err.(*aws.ErrorResponse); ok && awsErr.Code == "InvalidInstanceID.NotFound" { - fmt.Println("Instance not yet available, retrying...") - } else { - fmt.Println("Instance not yet available, retrying...") - //return fmt.Errorf("error checking instance status: %v", err) - } - } else { - allRunning := true - for _, reservation := range resp.Reservations { - for _, instance := range reservation.Instances { - if instance.State.Name != "running" { - allRunning = false - break - } - } - if !allRunning { - break - } - } - - if allRunning { - fmt.Println("EC2 instances initialized: ", instanceIDs) - return nil - } - } - - // Implement a non-blocking wait without using time.Sleep - start := time.Now() - waitDuration := 5 * time.Second - for { - elapsed := time.Since(start) - if elapsed >= waitDuration { - break - } - } - } - fmt.Println("Timeout waiting for running instances") - - return fmt.Errorf("timeout waiting for instances to reach running state") -} diff --git a/backend/nvms/lib/awspin/ec2/ec2_lib.go b/backend/nvms/lib/awspin/ec2/ec2_lib.go deleted file mode 100644 index b4a42b8a0..000000000 --- a/backend/nvms/lib/awspin/ec2/ec2_lib.go +++ /dev/null @@ -1,401 +0,0 @@ -package ec2 - -import ( - "context" - "encoding/hex" - "encoding/xml" - "fmt" - "net/http" - "net/url" - aws "nvms/lib/awspin" - "sort" - "strings" - "time" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -// NewEC2 creates a new EC2 Client -func NewEC2(config aws.Config) (*Client, error) { - u, err := url.Parse(config.Endpoint) - if err != nil { - return nil, fmt.Errorf("failed to parse endpoint: %w", err) - } - usePathStyle := strings.Contains(u.Host, "localhost") || strings.Contains(u.Host, "127.0.0.1") - - client := &Client{ - config: config, - endpointURL: u.String(), - usePathStyle: usePathStyle, - } - - return client, nil -} -func (c *Client) buildEndpoint(action string) (string, error) { - u, err := url.Parse(c.endpointURL) - if err != nil { - return "", fmt.Errorf("failed to parse endpoint: %w", err) - } - - if c.usePathStyle { - // LocalStack: http://localhost:4566/elasticloadbalancing/ - u = u.JoinPath("elasticloadbalancing") - } - - // Both AWS and LocalStack use query parameters for ELB API - q := u.Query() - q.Set("Action", action) - q.Set("Version", "2015-12-01") // ELB API version - u.RawQuery = q.Encode() - - return u.String(), nil -} - -func (c *Client) newRequest(ctx context.Context, method string, params map[string]string, body []byte) (*http.Request, error) { - furl, err := c.buildEndpoint(params["Action"]) - if err != nil { - return nil, err - } - u, err := url.Parse(furl) - if err != nil { - return nil, err - } - - var awsDate aws.AwsDate - awsDate.Time = time.Now() - - // Add required AWS Query API parameters - params["Version"] = "2016-11-15" - params["X-Amz-Algorithm"] = "AWS4-HMAC-SHA256" - params["X-Amz-Date"] = awsDate.GetTime() - - // Build credential scope - credentialScope := fmt.Sprintf("%s/%s/%s/aws4_request", - awsDate.GetDate(), - c.config.Region, - c.config.Service) - - params["X-Amz-Credential"] = fmt.Sprintf("%s/%s", - c.config.AccessKeyId, - credentialScope) - - // Set signed headers - params["X-Amz-SignedHeaders"] = "host" - - // Add security token if present - if c.config.SessionToken != "" { - params["X-Amz-Security-Token"] = c.config.SessionToken - } - - // Build canonical query string for signing - canonicalQueryString := GetCanonicalQueryString(params) - - // Create string to sign - canonicalRequest := strings.Join([]string{ - method, - "/", - canonicalQueryString, - fmt.Sprintf("host:%s\n", u.Host), // Canonical headers - "host", // Signed headers - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", // Empty payload hash - }, "\n") - - stringToSign := strings.Join([]string{ - "AWS4-HMAC-SHA256", - awsDate.GetTime(), - credentialScope, - aws.GetSHA256Hash([]byte(canonicalRequest)), - }, "\n") - - // Calculate signature - dateKey := aws.HmacSHA256([]byte("AWS4"+c.config.SecretAccessKey), []byte(awsDate.GetDate())) - regionKey := aws.HmacSHA256(dateKey, []byte(c.config.Region)) - serviceKey := aws.HmacSHA256(regionKey, []byte(c.config.Service)) - signingKey := aws.HmacSHA256(serviceKey, []byte("aws4_request")) - signature := hex.EncodeToString(aws.HmacSHA256(signingKey, []byte(stringToSign))) - - // Add signature to parameters - params["X-Amz-Signature"] = signature - - // Build final URL with all parameters - query := u.Query() - for k, v := range params { - query.Set(k, v) - } - u.RawQuery = query.Encode() - - //fmt.Printf("Request URL: %s\n", u.String()) - - // Create request with minimal headers - req, err := http.NewRequestWithContext(ctx, method, u.String(), nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("host", u.Host) - req.Header.Set("user-agent", "byteport") - - //fmt.Printf("Request headers: %+v\n", req.Header) - return req, nil -} - -// Helper function to create canonical query string -func GetCanonicalQueryString(params map[string]string) string { - // Get sorted list of parameter names - paramNames := make([]string, 0, len(params)) - for name := range params { - paramNames = append(paramNames, name) - } - sort.Strings(paramNames) - - // Build canonical query string - pairs := make([]string, 0, len(params)) - for _, name := range paramNames { - pairs = append(pairs, fmt.Sprintf("%s=%s", - url.QueryEscape(name), - url.QueryEscape(params[name]), - )) - } - - return strings.Join(pairs, "&") -} - -func (c *Client) do(req *http.Request) (*http.Response, error) { - resp, err := spinhttp.Send(req) - if err != nil { - fmt.Println("Error sending request: ", err) - return nil, fmt.Errorf("failed to send request: %w", err) - } - - if resp.StatusCode != http.StatusOK { - fmt.Println("Code: ", resp.StatusCode) - fmt.Println("Response: ", resp) - var errorResponse aws.ErrorResponse - if err := xml.NewDecoder(resp.Body).Decode(&errorResponse); err != nil { - fmt.Println("Error parsing response: ", err) - return nil, fmt.Errorf("failed to parse error response: %w", err) - } - fmt.Println("Error response: ", errorResponse) - return nil, errorResponse - } - fmt.Println("Request sent successfully") - return resp, nil -} - -// Client provides an interface for interacting with the EC2 API -type Client struct { - config aws.Config - endpointURL string - usePathStyle bool -} - -// Instance represents an EC2 instance -type Instance struct { - InstanceId string `xml:"instanceId"` - ImageId string `xml:"imageId"` - State struct { - Code int `xml:"code"` - Name string `xml:"name"` - } `xml:"instanceState"` - PrivateDnsName string `xml:"privateDnsName"` - DnsName string `xml:"dnsName"` - Reason string `xml:"reason"` - KeyName string `xml:"keyName"` - AmiLaunchIndex int `xml:"amiLaunchIndex"` - ProductCodes []string `xml:"productCodes"` - InstanceType string `xml:"instanceType"` - LaunchTime string `xml:"launchTime"` - Placement struct { - AvailabilityZone string `xml:"availabilityZone"` - GroupName string `xml:"groupName"` - } `xml:"placement"` - Monitoring struct { - State string `xml:"state"` - } `xml:"monitoring"` - SubnetId string `xml:"subnetId"` - VpcId string `xml:"vpcId"` - PrivateIpAddress string `xml:"privateIpAddress"` - SourceDestCheck bool `xml:"sourceDestCheck"` - GroupSet []struct { - GroupId string `xml:"groupId"` - GroupName string `xml:"groupName"` - } `xml:"groupSet>item"` - Architecture string `xml:"architecture"` - RootDeviceType string `xml:"rootDeviceType"` - RootDeviceName string `xml:"rootDeviceName"` - BlockDeviceMapping []struct { - DeviceName string `xml:"deviceName"` - Ebs struct { - VolumeId string `xml:"volumeId"` - Status string `xml:"status"` - AttachTime string `xml:"attachTime"` - DeleteOnTermination bool `xml:"deleteOnTermination"` - } `xml:"ebs"` - } `xml:"blockDeviceMapping>item"` - VirtualizationType string `xml:"virtualizationType"` - ClientToken string `xml:"clientToken"` - TagSet []struct { - Key string `xml:"key"` - Value string `xml:"value"` - } `xml:"tagSet>item"` - Hypervisor string `xml:"hypervisor"` - NetworkInterfaceSet []struct { - NetworkInterfaceId string `xml:"networkInterfaceId"` - SubnetId string `xml:"subnetId"` - VpcId string `xml:"vpcId"` - Description string `xml:"description"` - OwnerId string `xml:"ownerId"` - Status string `xml:"status"` - MacAddress string `xml:"macAddress"` - PrivateIpAddress string `xml:"privateIpAddress"` - SourceDestCheck bool `xml:"sourceDestCheck"` - GroupSet []struct { - GroupId string `xml:"groupId"` - GroupName string `xml:"groupName"` - } `xml:"groupSet>item"` - Attachment struct { - AttachmentId string `xml:"attachmentId"` - DeviceIndex int `xml:"deviceIndex"` - Status string `xml:"status"` - AttachTime string `xml:"attachTime"` - DeleteOnTermination bool `xml:"deleteOnTermination"` - } `xml:"attachment"` - PrivateIpAddressesSet []struct { - PrivateIpAddress string `xml:"privateIpAddress"` - Primary bool `xml:"primary"` - } `xml:"privateIpAddressesSet>item"` - } `xml:"networkInterfaceSet>item"` - EbsOptimized bool `xml:"ebsOptimized"` -} - -// RunInstancesResponse represents the response from RunInstances -type RunInstancesResponse struct { - XMLName xml.Name `xml:"RunInstancesResponse"` - ReservationId string `xml:"reservationId"` - OwnerId string `xml:"ownerId"` - Instances []Instance `xml:"instancesSet>item"` -} - -// DescribeInstancesResponse represents the response from DescribeInstances -type DescribeInstancesResponse struct { - XMLName xml.Name `xml:"DescribeInstancesResponse"` - Reservations []struct { - Instances []Instance `xml:"instancesSet>item"` - } `xml:"reservationSet>item"` -} -type DescribeVpcsResponse struct { - XMLName xml.Name `xml:"DescribeVpcsResponse"` - Vpcs []struct { - VpcId string `xml:"vpcId"` - OwnerId string `xml:"ownerId"` - State string `xml:"state"` - CidrBlock string `xml:"cidrBlock"` - CidrBlockAssociationSet []struct { - CidrBlock string `xml:"cidrBlock"` - AssociationId string `xml:"associationId"` - CidrBlockState struct { - State string `xml:"state"` - } `xml:"cidrBlockState"` - } `xml:"cidrBlockAssociationSet>item"` - DhcpOptionsId string `xml:"dhcpOptionsId"` - TagSet []struct { - Key string `xml:"key"` - Value string `xml:"value"` - } `xml:"tagSet>item"` - InstanceTenancy string `xml:"instanceTenancy"` - IsDefault bool `xml:"isDefault"` - } `xml:"vpcSet>item"` -} - -type DescribeSecurityGroupsResponse struct { - XMLName xml.Name `xml:"DescribeSecurityGroupsResponse"` - SecurityGroupInfo struct { - Item struct { - OwnerId string `xml:"ownerId"` - GroupId string `xml:"groupId"` - GroupName string `xml:"groupName"` - GroupDescription string `xml:"groupDescription"` - VpcId string `xml:"vpcId"` - IpPermissions []struct { - Item struct { - IpProtocol string `xml:"ipProtocol"` - FromPort int `xml:"fromPort"` - ToPort int `xml:"toPort"` - Groups []struct { - Item struct { - SecurityGroupRuleId string `xml:"securityGroupRuleId"` - UserId string `xml:"userId"` - GroupId string `xml:"groupId"` - VpcId string `xml:"vpcId"` - VpcPeeringConnectionId string `xml:"vpcPeeringConnectionId"` - PeeringStatus string `xml:"peeringStatus"` - } `xml:"item"` - } `xml:"groups"` - IpRanges []struct { - Item struct { - CidrIp string `xml:"cidrIp"` - } `xml:"item"` - } `xml:"ipRanges"` - PrefixListIds []struct { - Item struct { - PrefixListId string `xml:"prefixListId"` - } `xml:"item"` - } `xml:"prefixListIds"` - } `xml:"item"` - } `xml:"ipPermissions"` - IpPermissionsEgress []struct { - Item struct { - IpProtocol string `xml:"ipProtocol"` - Groups []struct { - Item struct { - SecurityGroupRuleId string `xml:"securityGroupRuleId"` - UserId string `xml:"userId"` - GroupId string `xml:"groupId"` - VpcId string `xml:"vpcId"` - VpcPeeringConnectionId string `xml:"vpcPeeringConnectionId"` - PeeringStatus string `xml:"peeringStatus"` - } `xml:"item"` - } `xml:"groups"` - IpRanges []struct { - Item struct { - CidrIp string `xml:"cidrIp"` - } `xml:"item"` - } `xml:"ipRanges"` - PrefixListIds []struct { - Item struct { - PrefixListId string `xml:"prefixListId"` - } `xml:"item"` - } `xml:"prefixListIds"` - } `xml:"item"` - } `xml:"ipPermissionsEgress"` - } `xml:"item"` - } `xml:"securityGroupInfo"` -} -type DescribeSubnetsResponse struct { - XMLName xml.Name `xml:"DescribeSubnetsResponse"` - SubnetSet []Subnet `xml:"subnetSet>item"` -} - -type Subnet struct { - SubnetId string `xml:"subnetId"` - SubnetArn string `xml:"subnetArn"` - State string `xml:"state"` - OwnerId string `xml:"ownerId"` - VpcId string `xml:"vpcId"` - CidrBlock string `xml:"cidrBlock"` - Ipv6CidrBlockAssociationSet []Ipv6CidrBlockAssociation `xml:"ipv6CidrBlockAssociationSet>item"` - AvailableIpAddressCount int `xml:"availableIpAddressCount"` - AvailabilityZone string `xml:"availabilityZone"` - AvailabilityZoneId string `xml:"availabilityZoneId"` - DefaultForAz bool `xml:"defaultForAz"` - MapPublicIpOnLaunch bool `xml:"mapPublicIpOnLaunch"` - AssignIpv6AddressOnCreation bool `xml:"assignIpv6AddressOnCreation"` -} - -type Ipv6CidrBlockAssociation struct { - Ipv6CidrBlock string `xml:"ipv6CidrBlock"` - AssociationId string `xml:"associationId"` - Ipv6CidrBlockState struct { - State string `xml:"state"` - } `xml:"ipv6CidrBlockState"` -} diff --git a/backend/nvms/lib/awspin/index.go b/backend/nvms/lib/awspin/index.go deleted file mode 100644 index f2e67aea6..000000000 --- a/backend/nvms/lib/awspin/index.go +++ /dev/null @@ -1,8 +0,0 @@ -// Package awspin provides AWS service abstractions for NVMS. -// -// # Subpackages -// -// ec2 - EC2 instance management -// network - VPC, ALB, and Route53 management -// s3 - S3 bucket operations -package awspin diff --git a/backend/nvms/lib/awspin/network/alb.go b/backend/nvms/lib/awspin/network/alb.go deleted file mode 100644 index b61a72c86..000000000 --- a/backend/nvms/lib/awspin/network/alb.go +++ /dev/null @@ -1,424 +0,0 @@ -package network - -import ( - "bytes" - "context" - "encoding/hex" - "encoding/xml" - "fmt" - "io" - "net/http" - "net/url" - aws "nvms/lib/awspin" - "sort" - "strconv" - "strings" - "time" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -func NewALB(config aws.Config) (*Client, error) { - u, err := url.Parse(config.Endpoint) - if err != nil { - return nil, fmt.Errorf("failed to parse endpoint: %w", err) - } - usePathStyle := strings.Contains(u.Host, "localhost") || strings.Contains(u.Host, "127.0.0.1") - - client := &Client{ - config: config, - endpointURL: u.String(), - usePathStyle: usePathStyle, - } - - return client, nil -} - -func (c *Client) newRequest(ctx context.Context, method string, params map[string]string, body []byte) (*http.Request, error) { - furl, err := c.buildEndpoint(params["Action"]) - if err != nil { - return nil, err - } - u, err := url.Parse(furl) - if err != nil { - return nil, err - } - - var awsDate aws.AwsDate - awsDate.Time = time.Now() - - params["Version"] = "2015-12-01" - params["X-Amz-Algorithm"] = "AWS4-HMAC-SHA256" - params["X-Amz-Date"] = awsDate.GetTime() - - // Build credential scope - credentialScope := fmt.Sprintf("%s/%s/%s/aws4_request", - awsDate.GetDate(), - c.config.Region, - c.config.Service) - - params["X-Amz-Credential"] = fmt.Sprintf("%s/%s", - c.config.AccessKeyId, - credentialScope) - - // Set signed headers - params["X-Amz-SignedHeaders"] = "host" - - // Add security token if present - if c.config.SessionToken != "" { - params["X-Amz-Security-Token"] = c.config.SessionToken - } - - // Build canonical query string for signing - canonicalQueryString := GetCanonicalQueryString(params) - - // Create string to sign - canonicalRequest := strings.Join([]string{ - method, - "/", - canonicalQueryString, - fmt.Sprintf("host:%s\n", u.Host), // Canonical headers - "host", // Signed headers - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", // Empty payload hash - }, "\n") - - stringToSign := strings.Join([]string{ - "AWS4-HMAC-SHA256", - awsDate.GetTime(), - credentialScope, - aws.GetSHA256Hash([]byte(canonicalRequest)), - }, "\n") - - // Calculate signature - dateKey := aws.HmacSHA256([]byte("AWS4"+c.config.SecretAccessKey), []byte(awsDate.GetDate())) - regionKey := aws.HmacSHA256(dateKey, []byte(c.config.Region)) - serviceKey := aws.HmacSHA256(regionKey, []byte(c.config.Service)) - signingKey := aws.HmacSHA256(serviceKey, []byte("aws4_request")) - signature := hex.EncodeToString(aws.HmacSHA256(signingKey, []byte(stringToSign))) - - // Add signature to parameters - params["X-Amz-Signature"] = signature - - // Build final URL with all parameters - query := u.Query() - for k, v := range params { - query.Set(k, v) - } - u.RawQuery = query.Encode() - - // Create request with minimal headers - req, err := http.NewRequestWithContext(ctx, method, u.String(), nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("host", u.Host) - req.Header.Set("user-agent", "byteport") - - fmt.Printf("ALB request prepared: method=%s host=%s action=%s\n", method, u.Host, params["Action"]) - - return req, nil -} -func (c *Client) buildEndpoint(action string) (string, error) { - u, err := url.Parse(c.endpointURL) - if err != nil { - return "", fmt.Errorf("failed to parse endpoint: %w", err) - } - - if c.usePathStyle { - // LocalStack: http://localhost:4566/elasticloadbalancing/ - u = u.JoinPath("elasticloadbalancing") - } - - // Both AWS and LocalStack use query parameters for ELB API - q := u.Query() - q.Set("Action", action) - q.Set("Version", "2015-12-01") // ELB API version - u.RawQuery = q.Encode() - - return u.String(), nil -} - -// Helper function to create canonical query string -func GetCanonicalQueryString(params map[string]string) string { - // Get sorted list of parameter names - paramNames := make([]string, 0, len(params)) - for name := range params { - paramNames = append(paramNames, name) - } - sort.Strings(paramNames) - - // Build canonical query string - pairs := make([]string, 0, len(params)) - for _, name := range paramNames { - pairs = append(pairs, fmt.Sprintf("%s=%s", - url.QueryEscape(name), - url.QueryEscape(params[name]), - )) - } - - return strings.Join(pairs, "&") -} - -func (c *Client) do(req *http.Request) (*http.Response, error) { - - resp, err := spinhttp.Send(req) - if err != nil { - fmt.Println("Error sending request: ", err) - return nil, fmt.Errorf("failed to send request: %w", err) - } - - if resp.StatusCode != http.StatusOK { - fmt.Println("Code: ", resp.StatusCode) - fmt.Println("Response: ", resp) - var errorResponse aws.ErrorResponse - if err := xml.NewDecoder(resp.Body).Decode(&errorResponse); err != nil { - fmt.Println("Error parsing response: ", err) - return nil, fmt.Errorf("failed to parse error response: %w", err) - } - fmt.Println("Error response: ", errorResponse) - return nil, errorResponse - } - fmt.Println("Request sent successfully") - return resp, nil -} -func (c *Client) CreateListener(ctx context.Context, name string, loadBalancerArn string, targetGroupArn string) (*CreateListenerResponse, error) { - fmt.Println("Creating listener: ", name) - fmt.Println("LoadBalancerArn: ", loadBalancerArn) - fmt.Println("TargetGroupArn: ", targetGroupArn) - params := map[string]string{ - "Action": "CreateListener", - "LoadBalancerArn": loadBalancerArn, - "Protocol": "HTTP", - "Port": "80", - "DefaultActions.member.1.Type": "forward", - "DefaultActions.member.1.TargetGroupArn": targetGroupArn, - "Version": "2015-12-01", - } - req, err := c.newRequest(ctx, http.MethodGet, params, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return nil, err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error creating listener: ", err) - return nil, err - - } - defer resp.Body.Close() - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) - } - //fmt.Println("Response Body:", string(bodyBytes)) - - // Reset the response body for decoding - resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - var listenerResponse CreateListenerResponse - if err := xml.NewDecoder(resp.Body).Decode(&listenerResponse); err != nil { - fmt.Println("Error creating request: ", err) - return nil, err - } - fmt.Println("Built Listener: ", listenerResponse.CreateListenerResult.Listeners.Member.ListenerArn) - return &listenerResponse, nil - -} -func (c *Client) CreateTargetGroup(ctx context.Context, name string, vpcId string) (string, error) { - fmt.Println("Creating target group: ", name) - serviceName := name - params := map[string]string{ - "Action": "CreateTargetGroup", - "Name": serviceName, - "Protocol": "HTTP", - "Port": "80", - "VpcId": vpcId, - "TargetType": "instance", - "Version": "2015-12-01", - } - req, err := c.newRequest(ctx, http.MethodGet, params, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return " ", err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error creating target group: ", err) - return " ", err - } - defer resp.Body.Close() - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("failed to read response body: %w", err) - } - //fmt.Println("Response Body:", string(bodyBytes)) - - // Reset the response body for decoding - resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - var targetGroupResponse CreateTargetGroupResponse - if err := xml.NewDecoder(resp.Body).Decode(&targetGroupResponse); err != nil { - fmt.Println("Error parsing response: ", err) - return " ", fmt.Errorf("failed to parse response: %w", err) - } - fmt.Println("Created target group: ", targetGroupResponse.CreateTargetGroupResult.TargetGroups.Member.TargetGroupArn) - return targetGroupResponse.CreateTargetGroupResult.TargetGroups.Member.TargetGroupArn, nil - -} -func (c *Client) RegisterTarget(ctx context.Context, targetGroupArn string, instanceId string, port int) error { - fmt.Println("Registering target: ", instanceId) - //fmt.Println("Port: ", port) - params := map[string]string{ - "Action": "RegisterTargets", - "TargetGroupArn": targetGroupArn, - "Targets.member.1.Id": instanceId, - "Targets.member.1.Port": strconv.Itoa(port), - "Version": "2015-12-01", - } - //fmt.Println("Port: ", port) - //fmt.Println("Str: ", string(port)) - req, err := c.newRequest(ctx, http.MethodGet, params, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error registering target: ", err) - return err - } - defer resp.Body.Close() - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - //fmt.Println("Response Body:", string(bodyBytes)) - - // Reset the response body for decoding - resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - fmt.Println("Target registered") - return nil - -} - -func (c *Client) CreateListenerRule(ctx context.Context, ListenerArn string, targetGroupArn string, serviceName string, priority int) error { - fmt.Println("Creating listener rule: ", serviceName) - params := map[string]string{ - "Action": "CreateRule", - "ListenerArn": ListenerArn, - "Priority": strconv.Itoa(priority), - "Conditions.member.1.Field": "path-pattern", - "Conditions.member.1.Values.member.1": "/" + serviceName + "/*", - "Actions.member.1.Type": "forward", - "Actions.member.1.TargetGroupArn": targetGroupArn, - "Version": "2015-12-01", - } - req, err := c.newRequest(ctx, http.MethodGet, params, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error creating listener rule: ", err) - return err - } - defer resp.Body.Close() - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - //fmt.Println("Response Body:", string(bodyBytes)) - - // Reset the response body for decoding - resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - fmt.Println("Rule Built") - return nil - -} -func (c *Client) CreateInternetApplicationLoadbalancer(ctx context.Context, name string, vpcsgId string, subnet1 string, subnet2 string) (*CreateLoadBalancerResponse, error) { - fmt.Println("Creating internet application load balancer: ", name) - req, err := c.newRequest(ctx, http.MethodPut, map[string]string{ - "Action": "CreateLoadBalancer", - "Name": name + "-byteport", - "Scheme": "internet-facing", - "Type": "application", - "Subnets.member.1": subnet1, - "Subnets.member.2": subnet2, - "SecurityGroups.member.1": vpcsgId, - }, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return nil, err - } - - resp, err := c.do(req) - if err != nil { - fmt.Println("Error creating internet application load balancer: ", err) - return nil, err - } - defer resp.Body.Close() - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) - } - //fmt.Println("Response Body:", string(bodyBytes)) - - // Reset the response body for decoding - resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - var albResponse CreateLoadBalancerResponse - if err := xml.NewDecoder(resp.Body).Decode(&albResponse); err != nil { - fmt.Println("Error parsing response: ", err) - return nil, fmt.Errorf("failed to parse response: %w", err) - } - fmt.Println("Created internet application load balancer: ", albResponse.CreateLoadBalancerResult.LoadBalancers.Member.LoadBalancerArn) - return &albResponse, nil -} -func (c *Client) DeleteLoadbalancer(ctx context.Context, arn string) error { - - req, err := c.newRequest(ctx, http.MethodPut, map[string]string{ - "Action": "DeleteLoadBalancer", - "LoadBalancerArn": arn, - }, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return err - } - - resp, err := c.do(req) - if err != nil { - fmt.Println("Error Deleting load balancer: ", err) - return err - } - defer resp.Body.Close() - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - //fmt.Println("Response Body:", string(bodyBytes)) - - // Reset the response body for decoding - resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - - return nil -} -func (c *Client) DeleteTargetGroup(ctx context.Context, arn string) error { - - params := map[string]string{ - "Action": "DeleteTargetGroup", - "TargetGroupArn": arn, - } - req, err := c.newRequest(ctx, http.MethodGet, params, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return err - } - resp, err := c.do(req) - if err != nil { - fmt.Println("Error Deleting target group: ", err) - return err - } - defer resp.Body.Close() - - return nil - -} diff --git a/backend/nvms/lib/awspin/network/lib.go b/backend/nvms/lib/awspin/network/lib.go deleted file mode 100644 index 4ecf0571a..000000000 --- a/backend/nvms/lib/awspin/network/lib.go +++ /dev/null @@ -1,128 +0,0 @@ -package network - -import ( - "encoding/xml" - aws "nvms/lib/awspin" -) - -type Client struct { - config aws.Config - endpointURL string - usePathStyle bool -} - -type CreateLoadBalancerResponse struct { - XMLName xml.Name `xml:"CreateLoadBalancerResponse"` - CreateLoadBalancerResult struct { - LoadBalancers struct { - Member struct { - LoadBalancerArn string `xml:"LoadBalancerArn"` - Scheme string `xml:"Scheme"` - AvailabilityZones struct { - Member []struct { - SubnetId string `xml:"SubnetId"` - ZoneName string `xml:"ZoneName"` - LoadBalancerAddresses string `xml:"LoadBalancerAddresses"` - } `xml:"member"` - } `xml:"AvailabilityZones"` - DNSName string `xml:"DNSName"` - Type string `xml:"Type"` - IpAddressType string `xml:"IpAddressType"` - LoadBalancerName string `xml:"LoadBalancerName"` - VpcId string `xml:"VpcId"` - CanonicalHostedZoneId string `xml:"CanonicalHostedZoneId"` - CreatedTime string `xml:"CreatedTime"` - SecurityGroups struct { - Member []string `xml:"member"` - } `xml:"SecurityGroups"` - State struct { - Code string `xml:"Code"` - } `xml:"State"` - } `xml:"member"` - } `xml:"LoadBalancers"` - } `xml:"CreateLoadBalancerResult"` - ResponseMetadata struct { - RequestId string `xml:"RequestId"` - } `xml:"ResponseMetadata"` -} - -type CreateListenerResponse struct { - XMLName xml.Name `xml:"CreateListenerResponse"` - CreateListenerResult struct { - Listeners struct { - Member struct { - LoadBalancerArn string `xml:"LoadBalancerArn"` - Protocol string `xml:"Protocol"` - Port string `xml:"Port"` - ListenerArn string `xml:"ListenerArn"` - DefaultActions struct { - Member []struct { - Type string `xml:"Type"` - TargetGroupArn string `xml:"TargetGroupArn"` - } `xml:"member"` - } `xml:"DefaultActions"` - } `xml:"member"` - } `xml:"Listeners"` - } `xml:"CreateListenerResult"` - ResponseMetadata struct { - RequestId string `xml:"RequestId"` - } `xml:"ResponseMetadata"` -} - -type CreateTargetGroupResponse struct { - XMLName xml.Name `xml:"CreateTargetGroupResponse"` - CreateTargetGroupResult struct { - TargetGroups struct { - Member struct { - TargetGroupArn string `xml:"TargetGroupArn"` - TargetGroupName string `xml:"TargetGroupName"` - Protocol string `xml:"Protocol"` - Port string `xml:"Port"` - VpcId string `xml:"VpcId"` - HealthCheckProtocol string `xml:"HealthCheckProtocol"` - HealthCheckPort string `xml:"HealthCheckPort"` - HealthCheckPath string `xml:"HealthCheckPath"` - HealthCheckTimeoutSeconds string `xml:"HealthCheckTimeoutSeconds"` - HealthyThresholdCount string `xml:"HealthyThresholdCount"` - UnhealthyThresholdCount string `xml:"UnhealthyThresholdCount"` - HealthCheckIntervalSeconds string `xml:"HealthCheckIntervalSeconds"` - Matcher struct { - HttpCode string `xml:"HttpCode"` - } `xml:"Matcher"` - } `xml:"member"` - } `xml:"TargetGroups"` - } `xml:"CreateTargetGroupResult"` - ResponseMetadata struct { - RequestId string `xml:"RequestId"` - } `xml:"ResponseMetadata"` -} - -type CreateRuleResponse struct { - XMLName xml.Name `xml:"CreateRuleResponse"` - CreateRuleResult struct { - Rules struct { - Member struct { - RuleArn string `xml:"RuleArn"` - Priority string `xml:"Priority"` - IsDefault string `xml:"IsDefault"` - Conditions struct { - Member []struct { - Field string `xml:"Field"` - Values struct { - Member []string `xml:"member"` - } `xml:"Values"` - } `xml:"member"` - } `xml:"Conditions"` - Actions struct { - Member []struct { - Type string `xml:"Type"` - TargetGroupArn string `xml:"TargetGroupArn"` - } `xml:"member"` - } `xml:"Actions"` - } `xml:"member"` - } `xml:"Rules"` - } `xml:"CreateRuleResult"` - ResponseMetadata struct { - RequestId string `xml:"RequestId"` - } `xml:"ResponseMetadata"` -} diff --git a/backend/nvms/lib/awspin/network/route53/lib.go b/backend/nvms/lib/awspin/network/route53/lib.go deleted file mode 100644 index f30d71392..000000000 --- a/backend/nvms/lib/awspin/network/route53/lib.go +++ /dev/null @@ -1,9 +0,0 @@ -package route53 - -import aws "nvms/lib/awspin" - -type Client struct { - config aws.Config - endpointURL string - usePathStyle bool -} diff --git a/backend/nvms/lib/awspin/network/route53/route53.go b/backend/nvms/lib/awspin/network/route53/route53.go deleted file mode 100644 index c33e5f7a1..000000000 --- a/backend/nvms/lib/awspin/network/route53/route53.go +++ /dev/null @@ -1,167 +0,0 @@ -package route53 - -import ( - "bytes" - "context" - "encoding/xml" - "fmt" - "net/http" - "net/url" - aws "nvms/lib/awspin" - "strings" - "time" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -// NewRoute53 initializes a Route 53 client. -func NewRoute53(config aws.Config) (*Client, error) { - u, err := url.Parse(config.Endpoint) - if err != nil { - return nil, fmt.Errorf("failed to parse endpoint: %w", err) - } - usePathStyle := strings.Contains(u.Host, "localhost") || strings.Contains(u.Host, "127.0.0.1") - - client := &Client{ - config: config, - endpointURL: u.String(), - usePathStyle: usePathStyle, - } - - return client, nil -} - -// CreateHostedZone creates a new private hosted zone. -func (c *Client) CreateHostedZone(ctx context.Context, domainName, region, vpcId string) (string, error) { - payload := fmt.Sprintf(` - - %s - %d - - false - - - %s - %s - -`, domainName, time.Now().Unix(), region, vpcId) - resp, err := c.newRequest(ctx, http.MethodPost, "", []byte(payload)) - if err != nil { - fmt.Printf("Failed to create hosteds zone: %v\n", err) - return "", fmt.Errorf("failed to create hosted zone: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { - fmt.Println("Failed to create hosted zone, status: ", resp.Status) - return "", fmt.Errorf("failed to create hosted zone, status: %s", resp.Status) - } - - type CreateHostedZoneResponse struct { - HostedZone struct { - ID string `xml:"Id"` - Name string `xml:"Name"` - } `xml:"HostedZone"` - } - var result CreateHostedZoneResponse - if err := xml.NewDecoder(resp.Body).Decode(&result); err != nil { - return "", fmt.Errorf("failed to parse response: %w", err) - } - fmt.Println("Hosted zone created successfully: ", result.HostedZone.Name) - // /hostedzone/id -> id - zoneID := strings.Split(result.HostedZone.ID, "/")[2] - return zoneID, nil -} - -// CreateRecordSet creates a new record set in the hosted zone. -func (c *Client) CreateRecordSet(ctx context.Context, hostedZoneID, name, recordType, value string, ttl int, projectName string) error { - payload := fmt.Sprintf(` - - - - - UPSERT - - %s - %s - %d - - - %s - - - - - - -`, name+"."+projectName+"-Byteport.", recordType, ttl, value) - - path := fmt.Sprintf("/%s/rrset", hostedZoneID) - fmt.Println("Sending Request: ", path) - fmt.Println("Payload: ", payload) - resp, err := c.newRequest(ctx, http.MethodPost, path, []byte(payload)) - if err != nil { - fmt.Println("response: ", resp) - return fmt.Errorf("failed to create record set: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("failed to create record set, status: %s", resp.Status) - } - - fmt.Println("Record set created successfully.") - return nil -} - -// buildEndpoint builds the request URL. -func (c *Client) buildEndpoint(path string) (string, error) { - u, err := url.Parse(c.endpointURL) - if err != nil { - return "", fmt.Errorf("failed to parse endpoint: %w", err) - } - return u.JoinPath(path).String(), nil -} - -// newRequest builds and signs a new HTTP request. -func (c *Client) newRequest(ctx context.Context, method, path string, body []byte) (*http.Response, error) { - endpoint, err := c.buildEndpoint(path) - if err != nil { - return nil, fmt.Errorf("failed to build endpoint: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, method, endpoint, bytes.NewReader(body)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - payloadHash := aws.GetPayloadHash(body) - awsDate := aws.AwsDate{Time: time.Now()} - req.Header.Set("host", req.URL.Host) - req.Header.Set("content-length", fmt.Sprintf("%d", len(body))) - req.Header.Set("x-amz-content-sha256", payloadHash) - req.Header.Set("x-amz-date", awsDate.GetTime()) - if c.config.SessionToken != "" { - req.Header.Set("x-amz-security-token", c.config.SessionToken) - } - req.Header.Set("authorization", aws.GetAuthorizationHeader(&c.config, req, &awsDate, payloadHash)) - fmt.Println("Request: ", req) - return c.do(req) -} - -// do sends the request and handles the response. -func (c *Client) do(req *http.Request) (*http.Response, error) { - resp, err := spinhttp.Send(req) - if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) - } - - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - var errorResponse aws.ErrorResponse - if err := xml.NewDecoder(resp.Body).Decode(&errorResponse); err != nil { - return nil, fmt.Errorf("failed to parse error response: %w", err) - } - return nil, errorResponse - } - return resp, nil -} diff --git a/backend/nvms/lib/awspin/s3/s3.go b/backend/nvms/lib/awspin/s3/s3.go deleted file mode 100644 index 6dfe016ce..000000000 --- a/backend/nvms/lib/awspin/s3/s3.go +++ /dev/null @@ -1,143 +0,0 @@ -package s3 - -import ( - "context" - "encoding/xml" - "fmt" - "io" - "net/http" -) - -// Client provides an interface for interacting with the S3 API. - -func (c *Client) CreateBucket(ctx context.Context, name string) error { - fmt.Println("Creating bucket: ", name) - req, err := c.newRequest(ctx, http.MethodPut, "", name, nil) - if err != nil { - fmt.Println("Error creating request: ", err) - return err - } - - resp, err := c.do(req) - if err != nil { - fmt.Println("Error creating bucket: ", err) - return err - } - defer resp.Body.Close() - return nil -} - -// ListBuckets returns a list of buckets. -func (c *Client) ListBuckets(ctx context.Context) (*ListBucketsResponse, error) { - req, err := c.newRequest(ctx, http.MethodGet, "", "", nil) - if err != nil { - return nil, err - } - - resp, err := c.do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - var results ListBucketsResponse - if err := xml.NewDecoder(resp.Body).Decode(&results); err != nil { - return nil, fmt.Errorf("failed to parse response: %w", err) - } - - return &results, nil -} - -// ListObjects returns a list of objects within a specified bucket. -func (c *Client) ListObjects(ctx context.Context, bucketName string) (*ListObjectsResponse, error) { - req, err := c.newRequest(ctx, http.MethodGet, bucketName, "", nil) - if err != nil { - return nil, err - } - - resp, err := c.do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - var results ListObjectsResponse - if err := xml.NewDecoder(resp.Body).Decode(&results); err != nil { - return nil, fmt.Errorf("failed to parse response: %w", err) - } - - return &results, nil -} - -// PutObject uploads an object to the specified bucket. -func (c *Client) PutObject(ctx context.Context, bucketName, objectName string, data []byte) error { - req, err := c.newRequest(ctx, http.MethodPut, bucketName, objectName, data) - if err != nil { - return err - } - - resp, err := c.do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} - -// GetObject fetches an object from the specified bucket. -// GetObject fetches an object from the specified bucket along with its metadata. -func (c *Client) GetObject(ctx context.Context, bucketName, objectName string) (io.ReadCloser, *ObjectMetadata, error) { - req, err := c.newRequest(ctx, http.MethodGet, bucketName, objectName, nil) - if err != nil { - return nil, nil, err - } - - resp, err := c.do(req) - if err != nil { - return nil, nil, err - } - - // Extract metadata from response headers - metadata := &ObjectMetadata{ - ETag: resp.Header.Get("ETag"), - LastModified: resp.Header.Get("Last-Modified"), - Size: resp.ContentLength, - ContentType: resp.Header.Get("Content-Type"), - StorageClass: resp.Header.Get("x-amz-storage-class"), - } - - return resp.Body, metadata, nil -} - -// DeleteObject deletes an object from the specified bucket. -func (c *Client) DeleteObject(ctx context.Context, bucketName, objectName string) error { - req, err := c.newRequest(ctx, http.MethodDelete, bucketName, objectName, nil) - if err != nil { - return err - } - - resp, err := c.do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} -func (c *Client) DeleteBucket(ctx context.Context, bucketName string) error { - req, err := c.newRequest(ctx, http.MethodDelete, bucketName, "", nil) - if err != nil { - return err - } - - resp, err := c.do(req) - if err != nil { - // check for NoSuchBucket error - fmt.Println("Error deleting bucket: ", err) - return err - } - defer resp.Body.Close() - - return nil -} diff --git a/backend/nvms/lib/awspin/s3/s3_lib.go b/backend/nvms/lib/awspin/s3/s3_lib.go deleted file mode 100644 index 4d656d407..000000000 --- a/backend/nvms/lib/awspin/s3/s3_lib.go +++ /dev/null @@ -1,167 +0,0 @@ -package s3 - -import ( - "bytes" - "context" - "encoding/xml" - "fmt" - "net/http" - "net/url" - aws "nvms/lib/awspin" - "strings" - "time" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -// https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html#API_ListBuckets_ResponseSyntax -type ListBucketsResponse struct { - Buckets []BucketInfo `xml:"Buckets>Bucket"` - Owner Owner -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/API_Bucket.html -type BucketInfo struct { - Name string - CreationDate time.Time -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html#API_ListObjects_ResponseSyntax -type ListObjectsResponse struct { - CommonPrefixes []CommonPrefix - Contents []ObjectInfo - Delimiter string - EncodingType string - IsTruncated bool - Marker string - MaxKeys int - Name string - NextMarker string - Prefix string -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/API_CommonPrefix.html -type CommonPrefix struct { - Prefix string -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/API_Object.html -type ObjectInfo struct { - Key string - ETag string - Size int - LastModified time.Time - StorageClass string - Owner Owner -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/API_Owner.html -type Owner struct { - DisplayName string - ID string -} - -// ObjectMetadata contains metadata about an S3 object. -// This struct is returned along with the object content when fetching objects. -type ObjectMetadata struct { - ETag string - LastModified time.Time - Size int64 - ContentType string - StorageClass string -} -type Client struct { - config aws.Config - endpointURL string - usePathStyle bool -} - -// New creates a new Client. -func NewS3(config aws.Config) (*Client, error) { - u, err := url.Parse(config.Endpoint) - if err != nil { - return nil, fmt.Errorf("failed to parse endpoint: %w", err) - } - usePathStyle := strings.Contains(u.Host, "localhost") || strings.Contains(u.Host, "127.0.0.1") - - client := &Client{ - config: config, - endpointURL: u.String(), - usePathStyle: usePathStyle, - } - - return client, nil -} -func (c *Client) buildEndpoint(bucketName, path string) (string, error) { - u, err := url.Parse(c.endpointURL) - if err != nil { - return "", fmt.Errorf("failed to parse endpoint: %w", err) - } - - if bucketName != "" { - if c.usePathStyle { - // LocalStack style: http://localhost:4566/bucket-name/path - u = u.JoinPath(bucketName) - } else { - // AWS style: http://bucket-name.s3.amazonaws.com/path - u.Host = bucketName + "." + u.Host - } - } - - if path != "" { - u = u.JoinPath(path) - } - - return u.String(), nil -} - -func (c *Client) newRequest(ctx context.Context, method, bucketName, path string, body []byte) (*http.Request, error) { - endpointURL, err := c.buildEndpoint(bucketName, path) - if err != nil { - return nil, err - } - - u, err := url.Parse(endpointURL) - if err != nil { - return nil, err - } - - req, err := http.NewRequestWithContext(ctx, method, endpointURL, bytes.NewReader(body)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - var awsDate aws.AwsDate - awsDate.Time = time.Now() - - payloadHash := aws.GetPayloadHash(body) - req.Header.Set("host", u.Host) - req.Header.Set("content-length", fmt.Sprintf("%d", len(body))) - req.Header.Set("x-amz-content-sha256", payloadHash) - req.Header.Set("x-amz-date", awsDate.GetTime()) - req.Header.Set("x-amz-security-token", c.config.SessionToken) - req.Header.Set("user-agent", "byteport") - req.Header.Set("authorization", aws.GetAuthorizationHeader(&c.config, req, &awsDate, payloadHash)) - //fmt.Println("Request: ", req) - return req, nil -} - -// do sends the request and handles any error response. -func (c *Client) do(req *http.Request) (*http.Response, error) { - resp, err := spinhttp.Send(req) - if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) - } - - // Only checking for a status of 200 feels too specific. - if resp.StatusCode != http.StatusOK { - var errorResponse aws.ErrorResponse - if err := xml.NewDecoder(resp.Body).Decode(&errorResponse); err != nil { - return nil, fmt.Errorf("failed to parse response: %w", err) - } - if resp.StatusCode != http.StatusNotFound { - return nil, errorResponse - } - } - return resp, nil -} diff --git a/backend/nvms/lib/awspin/types.go b/backend/nvms/lib/awspin/types.go deleted file mode 100644 index 72736b3fc..000000000 --- a/backend/nvms/lib/awspin/types.go +++ /dev/null @@ -1,46 +0,0 @@ -package awspin - -import ( - "fmt" - "time" -) - -// Config contains the available options for configuring a Client. -type Config struct { - // AWS Access key ID - AccessKeyId string - // AWS Secret Access key - SecretAccessKey string - // AWS Session Token - SessionToken string - // AWS region - Region string - // AWS Service - Service string - // Endpoint is an optional override URL to the s3 service. - Endpoint string -} - -type AwsDate struct { - Time time.Time -} - -func (d *AwsDate) GetDate() string { - return d.Time.UTC().Format("20060102") -} - -func (d *AwsDate) GetTime() string { - return d.Time.UTC().Format("20060102T150405Z") -} - -func (e ErrorResponse) Error() string { - return fmt.Sprintf("%s: %s", e.Code, e.Message) -} - -// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#RESTErrorResponses -type ErrorResponse struct { - Code string `xml:"Code"` - Message string `xml:"Message"` - Resource string `xml:"Resource"` - RequestID string `xml:"RequestId"` -} diff --git a/backend/nvms/lib/awsutil.go b/backend/nvms/lib/awsutil.go deleted file mode 100644 index 50c6a27ce..000000000 --- a/backend/nvms/lib/awsutil.go +++ /dev/null @@ -1,325 +0,0 @@ -package lib - -import ( - "fmt" - "log" - "nvms/models" - "path/filepath" - "strings" -) - -// S3DeploymentInfo contains information about deployed S3 resources -type S3DeploymentInfo struct { - BucketName string // Name of the created bucket - ObjectKey string // Key of the uploaded object (e.g., "src.zip") - Region string // AWS region where bucket was created - BucketARN string // ARN of the bucket - ObjectURL string // Full URL to access the object - ContentHash string // SHA256 hash of uploaded content -} - -// EC2InstanceInfo contains information about deployed EC2 instances -type EC2InstanceInfo struct { - InstanceID string // EC2 instance ID - PublicIP string // Public IP address - PrivateIP string // Private IP address - PublicDNS string // Public DNS name - PrivateDNS string // Private DNS name - State string // Current instance state - KeyPairName string // Name of the SSH key pair - SecurityGroups []string // List of security group IDs - SubnetID string // Subnet ID where instance is launched - Region string // AWS region where instance is launched -} -type BuilderReq struct { - ZipBall []byte `json:"zipball"` - AccessKey string `json:"accessKey"` - SecretKey string `json:"secretKey"` - ProjectName string `json:"projectName"` -} - -func getServiceEndpoint(service string) string { - if AWSEndpointBase == "http://localhost.localstack.cloud:4566" { - // LocalStack uses a single endpoint for all services - return AWSEndpointBase - } - // AWS uses service-specific endpoints - return fmt.Sprintf(AWSEndpointBase, service) -} -func DetectBuildPack(files []string, service models.Service) (*models.BuildPack, error) { - if service.BuildPack != nil { - fmt.Println("Service has buildpack") - // check if buildpack has required fields - if service.BuildPack.Name == "" || len(service.BuildPack.Build) == 0 || service.BuildPack.Start == "" || len(service.BuildPack.Packages) == 0 || len(service.BuildPack.PreBuild) == 0 { - fmt.Println("Buildpack is missing required parameters") - return nil, fmt.Errorf("buildpack is missing required parameters") - } - return service.BuildPack, nil - } - buildpacks := []models.BuildPack{ - { - Name: "Spin", - DetectFiles: []string{"spin.toml"}, - Packages: []string{"rust", "cargo", "golang", "spin"}, // Base requirements - PreBuild: []string{ - // Install Spin CLI - "curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash", - "mv spin /usr/local/bin/", - // Set up environment - "export SPIN_HOME=/root/.spin", - "mkdir -p $SPIN_HOME", - }, - Build: []string{ - // Build all components regardless of language - "spin build", - }, - Start: "spin up --listen 0.0.0.0", // Expose on port 80 - RuntimeVersions: map[string]string{ - "spin.toml": `spin_version = ["'](\d+\.\d+\.\d+)["']`, - }, - EnvVars: map[string]string{ - "SPIN_HOME": "/root/.spin", - "RUST_BACKTRACE": "1", // Helpful for debugging - "GOPATH": "/root/go", // For Go components - // Necessary for TinyGo compilation - "GOROOT": "/usr/local/go", - "TINYGO_ROOT": "/usr/local/tinygo", - }, - }, - { - Name: "Go", - DetectFiles: []string{"go.mod", "go.sum"}, - Packages: []string{"golang"}, - PreBuild: []string{ - "export HOME=/root", - "export XDG_CACHE_HOME=/root/go", - "export GOCACHE=/root/go/cache", - "export GOPATH=/root/go", - "export GOMODCACHE=$GOPATH/pkg/mod", - "mkdir -p $GOPATH", - "mkdir -p $GOCACHE", - "mkdir -p $XDG_CACHE_HOME", - }, - Build: []string{ - "go mod download", - "go build -o app", - }, - Start: "/app/$EXTRACT_DIR/$SERVICE_PATH/app", - RuntimeVersions: map[string]string{ - "go.mod": `go (\d+\.\d+)`, // Regex to extract version - }, - EnvVars: map[string]string{ - "GOPATH": "/root/go", - "GOMODCACHE": "/root/go/pkg/mod", - "GOCACHE": "/root/go/cache", - "HOME": "/root", - "XDG_CACHE_HOME": "/root/go", - }, - }, - { - Name: "Node.js", - DetectFiles: []string{"package.json", "yarn.lock", "npm-shrinkwrap.json"}, - Packages: []string{"nodejs", "npm"}, - PreBuild: []string{ - "npm install -g rollup", - "npm install -g yarn", // If yarn.lock exists - // Suppress npm update notification - "npm config set update-notifier false", - }, - Build: []string{ - "npm install", - "npm run build", - }, - Start: "npm start", - RuntimeVersions: map[string]string{ - "package.json": `"node": "(\d+\.\d+\.\d+)"`, - ".nvmrc": `^v?(\d+\.\d+\.\d+)$`, - }, - EnvVars: map[string]string{ - //"NPM_CONFIG_PRODUCTION": "true", - //"NODE_ENV": "production", - // Suppress npm update messages - "NO_UPDATE_NOTIFIER": "1", - }, - }, - { - Name: "Python", - DetectFiles: []string{"requirements.txt", "Pipfile", "pyproject.toml"}, - Packages: []string{"python3", "python3-pip", "python3-venv"}, - PreBuild: []string{ - "python3 -m venv venv", - "source venv/bin/activate", - }, - Build: []string{ - "pip install -r requirements.txt", - }, - Start: "python app.py", - RuntimeVersions: map[string]string{ - "runtime.txt": `python-(\d+\.\d+\.\d+)`, - "Pipfile": `python_version = "(\d+\.\d+)"`, - }, - EnvVars: map[string]string{ - "PYTHONPATH": "/app", - }, - }, - { - Name: "Java", - DetectFiles: []string{"pom.xml", "build.gradle", ".mvn"}, - Packages: []string{"java-11-openjdk", "maven"}, - PreBuild: []string{}, - Build: []string{ - "mvn clean install", - }, - Start: "java -jar target/*.jar", - RuntimeVersions: map[string]string{ - "system.properties": `java.runtime.version=(\d+)`, - }, - EnvVars: map[string]string{ - "JAVA_OPTS": "-Xmx300m -Xss512k -XX:CICompilerCount=2", - }, - }, - { - Name: "Ruby", - DetectFiles: []string{"Gemfile", "config.ru", "Rakefile"}, - Packages: []string{"ruby", "ruby-devel", "gcc", "make"}, - PreBuild: []string{ - "gem install bundler", - }, - Build: []string{ - "bundle install", - }, - Start: "bundle exec ruby app.rb", - RuntimeVersions: map[string]string{ - "Gemfile": `ruby ['\"](\d+\.\d+\.\d+)['\"]`, - ".ruby-version": `^(\d+\.\d+\.\d+)`, - }, - EnvVars: map[string]string{ - "RACK_ENV": "production", - }, - }, - { - Name: "PHP", - DetectFiles: []string{"composer.json", "index.php", "artisan"}, - Packages: []string{"php", "php-fpm", "php-mysql", "composer"}, - PreBuild: []string{}, - Build: []string{ - "composer install --no-dev", - }, - Start: "php-fpm", - RuntimeVersions: map[string]string{ - "composer.json": `"php": ["']>=?(\d+\.\d+)`, - }, - EnvVars: map[string]string{ - "PHP_FPM_PM": "dynamic", - }, - }, - { - Name: "Rust", - DetectFiles: []string{"Cargo.toml", "Cargo.lock"}, - Packages: []string{"rust", "cargo"}, - PreBuild: []string{}, - Build: []string{ - "cargo build --release", - }, - Start: "./target/release/app", - RuntimeVersions: map[string]string{ - "rust-toolchain.toml": `channel = ["'](\d+\.\d+)["']`, - }, - EnvVars: map[string]string{ - "RUST_BACKTRACE": "1", - }, - }, - } - fmt.Println("Checking buildpacks") - tree, rootDir, err := AnalyzeBuildpackPaths(files) - if err != nil { - fmt.Println("Error analyzing buildpack paths: ", err) - return nil, err - } - - // Check files in memory instead of on disk - for _, bp := range buildpacks { - if matchesBuildpackInMemory(tree, bp.DetectFiles, service.Path, rootDir) { - return &bp, nil - } - } - fmt.Println("No buildpack detected") - - return nil, fmt.Errorf("no buildpack detected for provided files") -} -func matchesBuildpackInMemory(tree map[string][]string, detectFiles []string, servicePath string, rootDir string) bool { - normalizedPath := filepath.Base(strings.Trim(servicePath, "/")) // Get just 'backend' or 'frontend' - //fmt.Printf("Checking buildpacks for path: %s\n", normalizedPath) - //fmt.Printf("Files to detect: %v\n", detectFiles) - //fmt.Printf("Tree: %+v\n", tree) - - // Get files in the service directory - dirFiles, exists := tree[normalizedPath] - if !exists { - fmt.Printf("Directory %s not found in tree\n", normalizedPath) - return false - } - - //fmt.Printf("Files in %s: %v\n", normalizedPath, dirFiles) - - // Check each detect file - for _, file := range detectFiles { - for _, f := range dirFiles { - if f == file { - fmt.Printf("Found matching file %s in %s\n", file, normalizedPath) - return true - } - } - } - - fmt.Printf("No matching files found in %s\n", normalizedPath) - return false -} -func AnalyzeBuildpackPaths(paths []string) (map[string][]string, string, error) { - // Extract root directory - rootDir := findCommonPrefix(paths) - if rootDir == "" { - return nil, "", fmt.Errorf("no common root directory found") - } - - // Create file tree - tree := make(map[string][]string) - for _, path := range paths { - relativePath := strings.TrimPrefix(path, rootDir) - dir := filepath.Dir(relativePath) - tree[dir] = append(tree[dir], filepath.Base(relativePath)) - } - - return tree, rootDir, nil -} - -func findCommonPrefix(paths []string) string { - if len(paths) == 0 { - return "" - } - - prefix := paths[0] - for _, path := range paths[1:] { - for !strings.HasPrefix(path, prefix) { - prefix = prefix[:strings.LastIndex(prefix, "/")] - } - } - return prefix -} -func GetAWSCredentials(user models.User) (string, string, error) { - eAccKey := user.AwsCreds.AccessKeyID - eSecKey := user.AwsCreds.SecretAccessKey - - accesskey, err := DecryptSecret(eAccKey) - if err != nil { - - return "", "", fmt.Errorf("Error decrypting access key") - } - secretkey, err := DecryptSecret(eSecKey) - if err != nil { - - return "", "", fmt.Errorf("Error decrypting secret key") - } - return accesskey, secretkey, nil - -} diff --git a/backend/nvms/lib/docker.go b/backend/nvms/lib/docker.go deleted file mode 100644 index a5012ff1f..000000000 --- a/backend/nvms/lib/docker.go +++ /dev/null @@ -1,192 +0,0 @@ -// lib/docker.go - Docker CLI management for BytePort Windows. -package lib - -import ( - "fmt" - "os" - "path/filepath" - "sync" - - "nvms/models" -) - -type DockerManager struct { - networkName string - mutex sync.RWMutex -} - -type DockerInstanceInfo struct { - ContainerID string `json:"container_id"` - Name string `json:"name"` - Port int `json:"port"` - Status string `json:"status"` - ProjectName string `json:"project_name"` - ServiceName string `json:"service_name"` - ImageTag string `json:"image_tag"` - InstanceID string `json:"instance_id"` - Region string `json:"region"` -} - -var dockerManagerInstance *DockerManager -var dockerManagerOnce sync.Once - -func GetDockerManager() (*DockerManager, error) { - var err error - dockerManagerOnce.Do(func() { - dockerManagerInstance, err = NewDockerManager() - }) - return dockerManagerInstance, err -} - -func NewDockerManager() (*DockerManager, error) { - dm := &DockerManager{networkName: "byteport-network"} - return dm, nil -} - -func (dm *DockerManager) ensureNetwork() error { - return nil -} - -func (dm *DockerManager) CreateAndStartContainer(service models.Service, projectPath string) (*DockerInstanceInfo, error) { - dm.mutex.Lock() - defer dm.mutex.Unlock() - - imageTag := fmt.Sprintf("byteport-%s-%s:latest", service.ProjectName, service.Name) - if err := dm.buildImage(projectPath, service.Path, imageTag, service); err != nil { - return nil, fmt.Errorf("failed to prepare Docker image inputs: %w", err) - } - - containerName := fmt.Sprintf("byteport-%s-%s", service.ProjectName, service.Name) - return &DockerInstanceInfo{ - ContainerID: containerName, - Name: containerName, - Port: service.Port, - Status: "prepared", - ProjectName: service.ProjectName, - ServiceName: service.Name, - ImageTag: imageTag, - InstanceID: containerName, - Region: "local", - }, nil -} - -func (dm *DockerManager) removeExistingContainer(containerName string) { -} - -func (dm *DockerManager) buildImage(projectPath, servicePath, imageTag string, service models.Service) error { - fullServicePath := filepath.Join(projectPath, servicePath) - dockerfilePath := filepath.Join(fullServicePath, "Dockerfile") - if _, err := os.Stat(dockerfilePath); os.IsNotExist(err) { - dockerfile := dm.generateDockerfile(fullServicePath, service) - if err := os.WriteFile(dockerfilePath, []byte(dockerfile), 0644); err != nil { - return fmt.Errorf("failed to create Dockerfile: %w", err) - } - } - - return dm.writeDockerCommandFile(fullServicePath, imageTag, service) -} - -func (dm *DockerManager) generateDockerfile(servicePath string, service models.Service) string { - if dm.fileExists(filepath.Join(servicePath, "package.json")) { - return dm.generateNodeDockerfile(service.Port) - } - if dm.fileExists(filepath.Join(servicePath, "go.mod")) { - return dm.generateGoDockerfile(service.Port) - } - if dm.fileExists(filepath.Join(servicePath, "requirements.txt")) { - return dm.generatePythonDockerfile(service.Port) - } - if dm.fileExists(filepath.Join(servicePath, "Cargo.toml")) { - return dm.generateRustDockerfile(service.Port) - } - return dm.generateNodeDockerfile(service.Port) -} - -func (dm *DockerManager) generateNodeDockerfile(port int) string { - return fmt.Sprintf(`FROM node:18-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm install -COPY . . -EXPOSE %d -CMD ["npm", "start"]`, port) -} - -func (dm *DockerManager) generateGoDockerfile(port int) string { - return fmt.Sprintf(`FROM golang:1.21-alpine AS builder -WORKDIR /app -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN go build -o main . - -FROM alpine:latest -RUN apk --no-cache add ca-certificates -WORKDIR /root/ -COPY --from=builder /app/main . -EXPOSE %d -CMD ["./main"]`, port) -} - -func (dm *DockerManager) generatePythonDockerfile(port int) string { - return fmt.Sprintf(`FROM python:3.11-slim -WORKDIR /app -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt -COPY . . -EXPOSE %d -CMD ["python", "app.py"]`, port) -} - -func (dm *DockerManager) generateRustDockerfile(port int) string { - return fmt.Sprintf(`FROM rust:1.70 AS builder -WORKDIR /app -COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo "fn main() {}" > src/main.rs -RUN cargo build --release -COPY src ./src -RUN cargo build --release - -FROM debian:bookworm-slim -WORKDIR /app -COPY --from=builder /app/target/release/app . -EXPOSE %d -CMD ["./app"]`, port) -} - -func (dm *DockerManager) StopContainer(containerID string) error { - return nil -} - -func (dm *DockerManager) RemoveContainer(containerID string) error { - return nil -} - -func (dm *DockerManager) GetContainerStatus(containerID string) (string, error) { - return "prepared", nil -} - -func (dm *DockerManager) ListProjectContainers(projectName string) ([]DockerInstanceInfo, error) { - return []DockerInstanceInfo{}, nil -} - -func (dm *DockerManager) fileExists(filename string) bool { - _, err := os.Stat(filename) - return !os.IsNotExist(err) -} - -func (dm *DockerManager) Close() error { - return nil -} - -func (dm *DockerManager) writeDockerCommandFile(servicePath, imageTag string, service models.Service) error { - containerName := fmt.Sprintf("byteport-%s-%s", service.ProjectName, service.Name) - commandText := fmt.Sprintf(`docker network create %[1]s 2>$null -docker build --tag %[2]s . -docker rm --force %[3]s 2>$null -docker run --detach --name %[3]s --network %[1]s --restart unless-stopped --publish %[4]d:%[4]d --workdir /app %[2]s -`, dm.networkName, imageTag, containerName, service.Port) - - commandPath := filepath.Join(servicePath, "byteport-docker.ps1") - return os.WriteFile(commandPath, []byte(commandText), 0644) -} diff --git a/backend/nvms/lib/index.go b/backend/nvms/lib/index.go deleted file mode 100644 index ee3b72785..000000000 --- a/backend/nvms/lib/index.go +++ /dev/null @@ -1,56 +0,0 @@ -// Package lib provides core functionality for NVMS (NVM Service) including -// AWS infrastructure management, authentication, and LLM provider integration. -// -// # Exports -// -// This package exports the following public functions: -// -// AWS Infrastructure Management: -// -// PushToS3(zipBall []byte, AccessKey, SecretKey, ProjectName string) (S3DeploymentInfo, error) -// DeployEC2(AccessKey, SecretKey string, bucket S3DeploymentInfo, service models.Service, fileMap []string) ([]EC2InstanceInfo, error) -// ProvisionNetwork(AccessKey, SecretKey, projectName string) (*awsnet.CreateLoadBalancerResponse, string, string, error) -// CreateALBListener(AccessKey, SecretKey, projectName, loadBalancerArn, vpcId, instanceId string, port int) (string, string, error) -// SetListenerRules(AccessKey, SecretKey, ListenerArn, TargetArn, serviceName string, priority int) error -// RegisterService(AccessKey, SecretKey, loadBalancerArn, projectName, serviceName, vpcId, instanceId string, port int) (string, error) -// AddNewRecord(AccessKey, SecretKey, domainName, zoneID, projectName, value string) (string, error) -// AwaitInitialization(AccessKey, SecretKey string, instanceIDs []string) error -// TerminateS3(resource models.AWSResource, AccessKey, SecretKey string) error -// TerminateEC2(resource models.AWSResource, AccessKey, SecretKey string) error -// TerminateALB(resource models.AWSResource, AccessKey, SecretKey string) error -// TerminateTargetGroup(resource models.AWSResource, AccessKey, SecretKey string) error -// -// Build Pack Detection: -// -// DetectBuildPack(files []string, service models.Service) (*models.BuildPack, error) -// -// Credential Management: -// -// GetAWSCredentials(user models.User) (string, string, error) -// -// Authentication: -// -// InitAuthSystem() error -// GenerateSymmetricKey() string -// GenerateNVMSToken(project models.Project) (string, error) -// ValidateServiceToken(encryptedToken string) (bool, *paseto.Token, error) -// AuthMiddleware(w http.ResponseWriter, r *http.Request) error -// EncryptSecret(secret string) (string, error) -// DecryptSecret(cipherText string) (string, error) -// GetDecodedEncryptionKey() ([]byte, error) -// -// LLM Provider Integration: -// -// RequestCompletion(prompt, strStruct string, config models.LLM) (string, error) -// -// Types: -// -// S3DeploymentInfo - Information about deployed S3 resources -// EC2InstanceInfo - Information about deployed EC2 instances -// BuilderReq - Request structure for building resources -// -// Subpackages: -// -// awspin - AWS service abstractions (S3, EC2, Network) -// providers - LLM provider implementations (OpenAI, Anthropic, Gemini, Local) -package lib diff --git a/backend/nvms/lib/llm.go b/backend/nvms/lib/llm.go deleted file mode 100644 index 06b194d7a..000000000 --- a/backend/nvms/lib/llm.go +++ /dev/null @@ -1,62 +0,0 @@ -package lib - -import ( - "errors" - "fmt" - lib "nvms/lib/providers" - "nvms/lib/providers/anthropic" - "nvms/lib/providers/gemini" - "nvms/lib/providers/local" - "nvms/lib/providers/openai" - "nvms/models" -) - -// Provider-specific errors -var ( - ErrProviderNotImplemented = errors.New("provider not implemented") -) - -const ( - ProviderOpenAI = "openai" - ProviderAnthropic = "anthropic" - ProviderGemini = "gemini" // Gemini provider implemented (lib/providers/gemini/gemini.go) - ProviderLocal = "local" -) - -func RequestCompletion(prompt string, strStruct string, config models.LLM) (string, error) { - fmt.Println("Strcut: ", strStruct) - var response string - var err error - - reqBody := lib.ChatRequest{ - Model: config.Providers[config.Provider].Modal, - Prompt: prompt, - ObjStruct: strStruct, - } - - switch config.Provider { - case ProviderOpenAI: - fmt.Println("OpenAI") - response, err = openai.RequestChatCompletion(reqBody, config.Providers[config.Provider].APIKey, config.Providers[config.Provider].Modal) - - case ProviderAnthropic: - fmt.Println("Anthropic") - response, err = anthropic.RequestChatCompletion(reqBody, config.Providers[config.Provider].APIKey, config.Providers[config.Provider].Modal) - - case ProviderGemini: - fmt.Println("Gemini") - response, err = gemini.RequestChatCompletion(reqBody, config.Providers[config.Provider].APIKey, config.Providers[config.Provider].Modal) - - case ProviderLocal: - fmt.Println("Local") - response, err = local.RequestCompletion(reqBody) - - default: - return "", fmt.Errorf("unknown provider: %s", config.Provider) - } - - if err != nil { - return "", fmt.Errorf("error sending request: %v", err) - } - return response, nil -} diff --git a/backend/nvms/lib/providers/anthropic/anthropic.go b/backend/nvms/lib/providers/anthropic/anthropic.go deleted file mode 100644 index fc8752c8f..000000000 --- a/backend/nvms/lib/providers/anthropic/anthropic.go +++ /dev/null @@ -1,86 +0,0 @@ -package anthropic - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - lib "nvms/lib/providers" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -const anthroEndpoint = "https://api.anthropic.com/v1/chat/completions" - -type anthMessage struct { - Role string `json:"role"` - Content string `json:"content"` -} - -type anthChatRequest struct { - Model string `json:"model"` - Messages []anthMessage `json:"messages"` - stream bool `json:"stream"` - //ResponseFormat FormatSchema `json:"response_format"` -} -type anthChoice struct { - Message struct { - Content string `json:"content"` - } `json:"message"` -} - -type anthChatResponse struct { - ID string `json:"id"` - Object string `json:"object"` - Created int64 `json:"created"` - Choices []struct { - Index int `json:"index"` - Message anthMessage `json:"message"` - } `json:"choices"` -} - -func RequestChatCompletion(reqBody lib.ChatRequest, key string, modal string) (string, error) { - - reqBase := anthChatRequest{ - Model: modal, - Messages: []anthMessage{ - { - Role: "user", - Content: reqBody.Prompt, - }, - }, - stream: false, - } - - jsonBody, err := json.Marshal(reqBase) - if err != nil { - return "", fmt.Errorf("error marshaling request: %v", err) - } - - req, err := http.NewRequest("POST", anthroEndpoint, bytes.NewBuffer(jsonBody)) - if err != nil { - return "", fmt.Errorf("error creating request: %v", err) - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+key) - - resp, err := spinhttp.Send(req) - if err != nil { - return "", fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - - var response anthChatResponse - fmt.Println("Response: ", resp) - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return "", fmt.Errorf("error decoding response: %v", err) - } - fmt.Println("Response: ", response) - - if len(response.Choices) == 0 { - return "", fmt.Errorf("no response choices returned") - } - - return response.Choices[0].Message.Content, nil -} diff --git a/backend/nvms/lib/providers/deepseek/deepseek.go b/backend/nvms/lib/providers/deepseek/deepseek.go deleted file mode 100644 index ae72e89a3..000000000 --- a/backend/nvms/lib/providers/deepseek/deepseek.go +++ /dev/null @@ -1,86 +0,0 @@ -package deepseek - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - lib "nvms/lib/providers" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -const deepseekEndpoint = "https://api.deepseek.ai/v1/chat/completions" - -type DSMessage struct { - Role string `json:"role"` - Content string `json:"content"` -} - -type DSChatRequest struct { - Model string `json:"model"` - Messages []DSMessage `json:"messages"` - stream bool `json:"stream"` - //ResponseFormat FormatSchema `json:"response_format"` -} -type DSChoice struct { - Message struct { - Content string `json:"content"` - } `json:"message"` -} - -type DSChatResponse struct { - ID string `json:"id"` - Object string `json:"object"` - Created int64 `json:"created"` - Choices []struct { - Index int `json:"index"` - Message DSMessage `json:"message"` - } `json:"choices"` -} - -func RequestChatCompletion(reqBody lib.ChatRequest, key string, modal string) (string, error) { - - reqBase := DSChatRequest{ - Model: "deepseek-chat", - Messages: []DSMessage{ - { - Role: "user", - Content: reqBody.Prompt, - }, - }, - stream: false, - } - - jsonBody, err := json.Marshal(reqBase) - if err != nil { - return "", fmt.Errorf("error marshaling request: %v", err) - } - - req, err := http.NewRequest("POST", deepseekEndpoint, bytes.NewBuffer(jsonBody)) - if err != nil { - return "", fmt.Errorf("error creating request: %v", err) - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+key) - - resp, err := spinhttp.Send(req) - if err != nil { - return "", fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - - var response DSChatResponse - fmt.Println("Response: ", resp) - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return "", fmt.Errorf("error decoding response: %v", err) - } - fmt.Println("Response: ", response) - - if len(response.Choices) == 0 { - return "", fmt.Errorf("no response choices returned") - } - - return response.Choices[0].Message.Content, nil -} diff --git a/backend/nvms/lib/providers/gemini/gemini.go b/backend/nvms/lib/providers/gemini/gemini.go deleted file mode 100644 index a6ad0b220..000000000 --- a/backend/nvms/lib/providers/gemini/gemini.go +++ /dev/null @@ -1,90 +0,0 @@ -package gemini - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - lib "nvms/lib/providers" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -const geminiEndpoint = "https://generativelanguage.googleapis.com/v1beta/models" - -type geminiMessage struct { - Role string `json:"role"` - Parts []struct { - Text string `json:"text"` - } `json:"parts"` -} - -type geminiChatRequest struct { - Contents []geminiMessage `json:"contents"` -} - -type geminiCandidate struct { - Content struct { - Parts []struct { - Text string `json:"text"` - } `json:"parts"` - } `json:"content"` -} - -type geminiChatResponse struct { - Candidates []geminiCandidate `json:"candidates"` -} - -func RequestChatCompletion(reqBody lib.ChatRequest, key string, modal string) (string, error) { - // Construct the full endpoint with API key - endpoint := fmt.Sprintf("%s/%s:generateContent?key=%s", geminiEndpoint, modal, key) - - // Build the request body - reqBase := geminiChatRequest{ - Contents: []geminiMessage{ - { - Role: "user", - Parts: []struct { - Text string `json:"text"` - }{ - {Text: reqBody.Prompt}, - }, - }, - }, - } - - jsonBody, err := json.Marshal(reqBase) - if err != nil { - return "", fmt.Errorf("error marshaling request: %v", err) - } - - req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonBody)) - if err != nil { - return "", fmt.Errorf("error creating request: %v", err) - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := spinhttp.Send(req) - if err != nil { - return "", fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - - var response geminiChatResponse - fmt.Printf("Gemini Response: %v\n", resp) - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return "", fmt.Errorf("error decoding response: %v", err) - } - fmt.Printf("Gemini Response: %v\n", response) - - if len(response.Candidates) == 0 { - return "", fmt.Errorf("no response candidates returned") - } - - if len(response.Candidates[0].Content.Parts) == 0 { - return "", fmt.Errorf("no content in response") - } - - return response.Candidates[0].Content.Parts[0].Text, nil -} diff --git a/backend/nvms/lib/providers/index.go b/backend/nvms/lib/providers/index.go deleted file mode 100644 index 7bce23dfd..000000000 --- a/backend/nvms/lib/providers/index.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package providers provides LLM provider implementations for NVMS. -// -// # Subpackages -// -// openai - OpenAI API integration -// anthropic - Anthropic Claude API integration -// local - Local LLM integration -// deepseek - DeepSeek API integration -package providers diff --git a/backend/nvms/lib/providers/lib.go b/backend/nvms/lib/providers/lib.go deleted file mode 100644 index c76656786..000000000 --- a/backend/nvms/lib/providers/lib.go +++ /dev/null @@ -1,18 +0,0 @@ -package providers - -type Message struct { - Role string `json:"role"` - Content string `json:"content"` -} - -type ChatRequest struct { - Model string `json:"model"` - Prompt string `json:"prompt"` - ObjStruct string `json:"objStruct"` -} - -type Choice struct { - Message struct { - Content string `json:"content"` - } `json:"message"` -} diff --git a/backend/nvms/lib/providers/local/local.go b/backend/nvms/lib/providers/local/local.go deleted file mode 100644 index 92f9aa0a0..000000000 --- a/backend/nvms/lib/providers/local/local.go +++ /dev/null @@ -1,66 +0,0 @@ -package local - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - lib "nvms/lib/providers" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -const localEndpoint = "http://localhost:11434/api/generate" - -type LocalChatRequest struct { - Model string `json:"model"` - Prompt string `json:"prompt"` - Stream bool `json:"stream"` - Format string `json:"format"` -} - -type LocalChatResponse struct { - Response string `json:"response"` -} - -func RequestCompletion(reqBody lib.ChatRequest) (string, error) { - var objStruct interface{} - if err := json.Unmarshal([]byte(reqBody.ObjStruct), &objStruct); err != nil { - fmt.Printf("error decoding objStruct: %v\n", err) - return "", fmt.Errorf("error decoding objStruct: %v", err) - } - fmt.Println("REQ: ", reqBody) - jsonBody, err := json.Marshal(LocalChatRequest{ - Model: reqBody.Model, - Prompt: reqBody.Prompt + "\n\n" + fmt.Sprintf("%v", objStruct), - Stream: false, - Format: "json", - }) - if err != nil { - fmt.Println("error marshaling request: %v", err) - return "", fmt.Errorf("error marshaling request: %v", err) - } - - req, err := http.NewRequest("POST", localEndpoint, bytes.NewBuffer(jsonBody)) - if err != nil { - fmt.Println("error creating request: %v", err) - return "", fmt.Errorf("error creating request: %v", err) - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := spinhttp.Send(req) - if err != nil { - fmt.Printf("error sending request: %v\n", err) - return "", fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - fmt.Println("RESP: ", resp) - var response LocalChatResponse - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - fmt.Printf("error decoding response: %v\n", err) - return "", fmt.Errorf("error decoding response: %v", err) - } - - return response.Response, nil -} diff --git a/backend/nvms/lib/providers/openai/openai.go b/backend/nvms/lib/providers/openai/openai.go deleted file mode 100644 index 8d5a73f8a..000000000 --- a/backend/nvms/lib/providers/openai/openai.go +++ /dev/null @@ -1,106 +0,0 @@ -package openai - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - lib "nvms/lib/providers" - - spinhttp "github.com/fermyon/spin-go-sdk/http" -) - -const oaiEndpoint = "https://api.openai.com/v1/chat/completions" - -/* - type FormatSchema struct { - Type string `json:"type"` - JsonSchema ResponseSchema `json:"json_schema"` - } - - type ResponseSchema struct { - Type string `json:"type"` - Properties map[string]interface{} `json:"properties"` - Required []string `json:"required"` - Schema string `json:"schema"` // Added required schema field - } -*/ -type OAIMessage struct { - Role string `json:"role"` - Content string `json:"content"` -} - -type OAIChatRequest struct { - Model string `json:"model"` - Messages []OAIMessage `json:"messages"` - //ResponseFormat FormatSchema `json:"response_format"` -} -type OAIChoice struct { - Message struct { - Content string `json:"content"` - } `json:"message"` -} - -type OAIChatResponse struct { - ID string `json:"id"` - Object string `json:"object"` - Created int64 `json:"created"` - Choices []struct { - Index int `json:"index"` - Message OAIMessage `json:"message"` - } `json:"choices"` -} - -func RequestChatCompletion(reqBody lib.ChatRequest, key string, modal string) (string, error) { - var objStruct interface{} - if err := json.Unmarshal([]byte(reqBody.ObjStruct), &objStruct); err != nil { - fmt.Printf("error decoding objStruct: %v\n", err) - return "", fmt.Errorf("error decoding objStruct: %v", err) - } - reqBase := OAIChatRequest{ - Model: modal, - Messages: []OAIMessage{ - { - Role: "user", - Content: reqBody.Prompt}}, - /*ResponseFormat: FormatSchema{ - Type: "json_object", - JsonSchema: ResponseSchema{ - Type: "object", - Properties: make(map[string]interface{}), - Required: []string{}, - Schema: "http://json-schema.org/draft-07/schema#", - }, - },*/} - jsonBody, err := json.Marshal(reqBase) - if err != nil { - return "", fmt.Errorf("error marshaling request: %v", err) - } - - req, err := http.NewRequest("POST", oaiEndpoint, bytes.NewBuffer(jsonBody)) - if err != nil { - return "", fmt.Errorf("error creating request: %v", err) - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+key) - - resp, err := spinhttp.Send(req) - if err != nil { - return "", fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - - var response OAIChatResponse - fmt.Println("Response: ", resp) - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return "", fmt.Errorf("error decoding response: %v", err) - } - fmt.Println("Response: ", response) - - if len(response.Choices) == 0 { - return "", fmt.Errorf("no response choices returned") - } - - return response.Choices[0].Message.Content, nil -} diff --git a/backend/nvms/lib/storage.go b/backend/nvms/lib/storage.go deleted file mode 100644 index 3165c1df8..000000000 --- a/backend/nvms/lib/storage.go +++ /dev/null @@ -1,308 +0,0 @@ -// lib/storage.go - Local storage management for BytePort Windows -package lib - -import ( - "archive/zip" - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "sync" - - "github.com/google/uuid" -) - -type StorageManager struct { - basePath string - mutex sync.RWMutex -} - -type LocalStorageInfo struct { - Path string `json:"path"` - ProjectID string `json:"project_id"` - BucketARN string `json:"bucket_arn"` // For compatibility - Region string `json:"region"` // For compatibility - BucketName string `json:"bucket_name"` // For compatibility -} - -var storageManagerInstance *StorageManager -var storageManagerOnce sync.Once - -func GetStorageManager() (*StorageManager, error) { - var err error - storageManagerOnce.Do(func() { - basePath := os.Getenv("PROJECTS_PATH") - if basePath == "" { - basePath = "C:\\BytePort\\projects" - } - storageManagerInstance, err = NewStorageManager(basePath) - }) - return storageManagerInstance, err -} - -func NewStorageManager(basePath string) (*StorageManager, error) { - sm := &StorageManager{ - basePath: basePath, - } - - // Ensure base directory exists - err := os.MkdirAll(basePath, 0755) - if err != nil { - return nil, fmt.Errorf("failed to create base directory: %w", err) - } - - return sm, nil -} - -// PushToLocalStorage - Replaces PushToS3 for local storage -func (sm *StorageManager) PushToLocalStorage(zipBall []byte, projectName string) (LocalStorageInfo, error) { - sm.mutex.Lock() - defer sm.mutex.Unlock() - - fmt.Println("Storing project locally...") - - // Create unique project directory - projectID := uuid.New().String() - projectPath := filepath.Join(sm.basePath, fmt.Sprintf("%s-%s", projectName, projectID)) - - err := os.MkdirAll(projectPath, 0755) - if err != nil { - return LocalStorageInfo{}, fmt.Errorf("failed to create project directory: %w", err) - } - - // Extract zip file to project directory - err = sm.extractZip(zipBall, projectPath) - if err != nil { - return LocalStorageInfo{}, fmt.Errorf("failed to extract project files: %w", err) - } - - fmt.Println("Project stored locally at:", projectPath) - - return LocalStorageInfo{ - Path: projectPath, - ProjectID: projectID, - BucketARN: projectPath, // For compatibility - Region: "local", // For compatibility - BucketName: fmt.Sprintf("%s-%s", projectName, projectID), // For compatibility - }, nil -} - -func (sm *StorageManager) extractZip(zipData []byte, destPath string) error { - reader, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData))) - if err != nil { - return fmt.Errorf("failed to create zip reader: %w", err) - } - - for _, file := range reader.File { - // Clean the file path to prevent directory traversal - cleanPath := filepath.Clean(file.Name) - if strings.Contains(cleanPath, "..") { - continue // Skip files with .. in path - } - - destFile := filepath.Join(destPath, cleanPath) - - // Create directory if needed - if file.FileInfo().IsDir() { - err := os.MkdirAll(destFile, file.FileInfo().Mode()) - if err != nil { - return fmt.Errorf("failed to create directory %s: %w", destFile, err) - } - continue - } - - // Create parent directory - err := os.MkdirAll(filepath.Dir(destFile), 0755) - if err != nil { - return fmt.Errorf("failed to create parent directory for %s: %w", destFile, err) - } - - // Extract file - err = sm.extractFile(file, destFile) - if err != nil { - return fmt.Errorf("failed to extract file %s: %w", file.Name, err) - } - } - - return nil -} - -func (sm *StorageManager) extractFile(file *zip.File, destPath string) error { - rc, err := file.Open() - if err != nil { - return err - } - defer rc.Close() - - outFile, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.FileInfo().Mode()) - if err != nil { - return err - } - defer outFile.Close() - - _, err = io.Copy(outFile, rc) - return err -} - -func (sm *StorageManager) RemoveProject(projectName string) error { - sm.mutex.Lock() - defer sm.mutex.Unlock() - - // Find and remove all directories that start with the project name - entries, err := os.ReadDir(sm.basePath) - if err != nil { - return fmt.Errorf("failed to read base directory: %w", err) - } - - for _, entry := range entries { - if entry.IsDir() && strings.HasPrefix(entry.Name(), projectName+"-") { - projectPath := filepath.Join(sm.basePath, entry.Name()) - err := os.RemoveAll(projectPath) - if err != nil { - fmt.Printf("Warning: failed to remove project directory %s: %v\n", projectPath, err) - } else { - fmt.Printf("Removed project directory: %s\n", projectPath) - } - } - } - - return nil -} - -func (sm *StorageManager) GetProjectPath(projectName, projectID string) string { - return filepath.Join(sm.basePath, fmt.Sprintf("%s-%s", projectName, projectID)) -} - -func (sm *StorageManager) ListProjects() ([]string, error) { - sm.mutex.RLock() - defer sm.mutex.RUnlock() - - entries, err := os.ReadDir(sm.basePath) - if err != nil { - return nil, fmt.Errorf("failed to read base directory: %w", err) - } - - var projects []string - for _, entry := range entries { - if entry.IsDir() { - projects = append(projects, entry.Name()) - } - } - - return projects, nil -} - -func (sm *StorageManager) ProjectExists(projectName, projectID string) bool { - projectPath := sm.GetProjectPath(projectName, projectID) - _, err := os.Stat(projectPath) - return !os.IsNotExist(err) -} - -func (sm *StorageManager) GetProjectSize(projectName, projectID string) (int64, error) { - projectPath := sm.GetProjectPath(projectName, projectID) - - var size int64 - err := filepath.Walk(projectPath, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - size += info.Size() - } - return nil - }) - - return size, err -} - -func (sm *StorageManager) CreateBackup(projectName, projectID string) (string, error) { - sm.mutex.RLock() - defer sm.mutex.RUnlock() - - projectPath := sm.GetProjectPath(projectName, projectID) - if _, err := os.Stat(projectPath); os.IsNotExist(err) { - return "", fmt.Errorf("project not found: %s", projectPath) - } - - // Create backup directory - backupDir := filepath.Join(sm.basePath, "backups") - err := os.MkdirAll(backupDir, 0755) - if err != nil { - return "", fmt.Errorf("failed to create backup directory: %w", err) - } - - // Create backup zip file - backupFile := filepath.Join(backupDir, fmt.Sprintf("%s-%s-backup.zip", projectName, projectID)) - - err = sm.createZipArchive(projectPath, backupFile) - if err != nil { - return "", fmt.Errorf("failed to create backup archive: %w", err) - } - - return backupFile, nil -} - -func (sm *StorageManager) createZipArchive(sourcePath, destPath string) error { - zipFile, err := os.Create(destPath) - if err != nil { - return err - } - defer zipFile.Close() - - zipWriter := zip.NewWriter(zipFile) - defer zipWriter.Close() - - return filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - // Skip directories - if info.IsDir() { - return nil - } - - // Get relative path - relPath, err := filepath.Rel(sourcePath, path) - if err != nil { - return err - } - - // Create zip file header - header, err := zip.FileInfoHeader(info) - if err != nil { - return err - } - header.Name = filepath.ToSlash(relPath) - - // Create writer for file - writer, err := zipWriter.CreateHeader(header) - if err != nil { - return err - } - - // Copy file content - file, err := os.Open(path) - if err != nil { - return err - } - defer file.Close() - - _, err = io.Copy(writer, file) - return err - }) -} - -// Compatibility function to match existing S3 interface -func PushToS3(zipBall []byte, accessKey, secretKey, projectName string) (LocalStorageInfo, error) { - sm, err := GetStorageManager() - if err != nil { - return LocalStorageInfo{}, err - } - return sm.PushToLocalStorage(zipBall, projectName) -} - -// Type alias for compatibility -type S3DeploymentInfo = LocalStorageInfo diff --git a/backend/nvms/lib/tunnel.go b/backend/nvms/lib/tunnel.go deleted file mode 100644 index 6361dd862..000000000 --- a/backend/nvms/lib/tunnel.go +++ /dev/null @@ -1,313 +0,0 @@ -// lib/tunnel.go - Cloudflare Tunnel management for BytePort Windows -package lib - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "sync" - - "gopkg.in/yaml.v2" - "nvms/models" -) - -type TunnelManager struct { - configPath string - tunnelName string - domain string - credentialsFile string - runningTunnels map[string]*TunnelProcess - mutex sync.RWMutex -} - -type TunnelProcess struct { - ProjectName string - ConfigFile string - URL string -} - -type TunnelConfig struct { - Tunnel string `yaml:"tunnel"` - CredentialsFile string `yaml:"credentials-file"` - Ingress []map[string]interface{} `yaml:"ingress"` - LogFile string `yaml:"logfile,omitempty"` -} - -var tunnelManagerInstance *TunnelManager -var tunnelManagerOnce sync.Once - -func GetTunnelManager() (*TunnelManager, error) { - var err error - tunnelManagerOnce.Do(func() { - // Default configuration - should be overridden by environment variables - configPath := os.Getenv("TUNNEL_CONFIG_PATH") - if configPath == "" { - configPath = "C:\\BytePort\\tunnels" - } - - tunnelName := os.Getenv("TUNNEL_NAME") - if tunnelName == "" { - tunnelName = "byteport-main" - } - - domain := os.Getenv("BYTEPORT_DOMAIN") - if domain == "" { - domain = "yourdomain.com" - } - - credentialsFile := filepath.Join(configPath, "credentials.json") - - tunnelManagerInstance, err = NewTunnelManager(configPath, tunnelName, domain, credentialsFile) - }) - return tunnelManagerInstance, err -} - -func NewTunnelManager(configPath, tunnelName, domain, credentialsFile string) (*TunnelManager, error) { - tm := &TunnelManager{ - configPath: configPath, - tunnelName: tunnelName, - domain: domain, - credentialsFile: credentialsFile, - runningTunnels: make(map[string]*TunnelProcess), - } - - // Ensure config directory exists - err := os.MkdirAll(configPath, 0755) - if err != nil { - return nil, fmt.Errorf("failed to create config directory: %w", err) - } - - return tm, nil -} - -func (tm *TunnelManager) CreateProjectTunnel(projectName string, services []models.Service) (string, error) { - tm.mutex.Lock() - defer tm.mutex.Unlock() - - // Generate tunnel configuration - config := TunnelConfig{ - Tunnel: tm.tunnelName, - CredentialsFile: tm.credentialsFile, - Ingress: tm.generateIngressRules(projectName, services), - LogFile: filepath.Join(tm.configPath, fmt.Sprintf("%s.log", projectName)), - } - - // Write configuration to file - configBytes, err := yaml.Marshal(config) - if err != nil { - return "", fmt.Errorf("failed to marshal tunnel config: %w", err) - } - - configFile := filepath.Join(tm.configPath, fmt.Sprintf("%s.yml", projectName)) - err = ioutil.WriteFile(configFile, configBytes, 0644) - if err != nil { - return "", fmt.Errorf("failed to write tunnel config: %w", err) - } - - // Generate project URL - projectURL := fmt.Sprintf("https://%s.%s", projectName, tm.domain) - - return projectURL, nil -} - -func (tm *TunnelManager) StartProjectTunnel(projectName string) (string, error) { - tm.mutex.Lock() - defer tm.mutex.Unlock() - - // Check if tunnel is already running - if tunnel, exists := tm.runningTunnels[projectName]; exists { - return tunnel.URL, nil - } - - configFile := filepath.Join(tm.configPath, fmt.Sprintf("%s.yml", projectName)) - if _, err := os.Stat(configFile); os.IsNotExist(err) { - return "", fmt.Errorf("tunnel configuration not found for project %s", projectName) - } - - projectURL := fmt.Sprintf("https://%s.%s", projectName, tm.domain) - - // Store configured tunnel info. Runtime startup is handled by the Windows setup scripts. - tm.runningTunnels[projectName] = &TunnelProcess{ - ProjectName: projectName, - ConfigFile: configFile, - URL: projectURL, - } - - return projectURL, nil -} - -func (tm *TunnelManager) StopProjectTunnel(projectName string) error { - tm.mutex.Lock() - defer tm.mutex.Unlock() - - tunnel, exists := tm.runningTunnels[projectName] - if !exists { - return fmt.Errorf("tunnel not found for project %s", projectName) - } - - _ = tunnel - delete(tm.runningTunnels, projectName) - - return nil -} - -func (tm *TunnelManager) RemoveProjectTunnel(projectName string) error { - // Stop tunnel if running - if _, exists := tm.runningTunnels[projectName]; exists { - err := tm.StopProjectTunnel(projectName) - if err != nil { - return err - } - } - - // Remove configuration file - configFile := filepath.Join(tm.configPath, fmt.Sprintf("%s.yml", projectName)) - err := os.Remove(configFile) - if err != nil && !os.IsNotExist(err) { - return fmt.Errorf("failed to remove tunnel config: %w", err) - } - - // Remove log file - logFile := filepath.Join(tm.configPath, fmt.Sprintf("%s.log", projectName)) - os.Remove(logFile) // Ignore errors for log file removal - - return nil -} - -func (tm *TunnelManager) GetProjectURL(projectName string) string { - tm.mutex.RLock() - defer tm.mutex.RUnlock() - - if tunnel, exists := tm.runningTunnels[projectName]; exists { - return tunnel.URL - } - - return fmt.Sprintf("https://%s.%s", projectName, tm.domain) -} - -func (tm *TunnelManager) ListRunningTunnels() map[string]string { - tm.mutex.RLock() - defer tm.mutex.RUnlock() - - result := make(map[string]string) - for projectName, tunnel := range tm.runningTunnels { - result[projectName] = tunnel.URL - } - - return result -} - -func (tm *TunnelManager) generateIngressRules(projectName string, services []models.Service) []map[string]interface{} { - var rules []map[string]interface{} - hostname := fmt.Sprintf("%s.%s", projectName, tm.domain) - - // Sort services to ensure 'main' comes first - var mainService *models.Service - var otherServices []models.Service - - for i, service := range services { - if service.Name == "main" { - mainService = &services[i] - } else { - otherServices = append(otherServices, service) - } - } - - // Add main service rule (root path) - if mainService != nil { - rules = append(rules, map[string]interface{}{ - "hostname": hostname, - "service": fmt.Sprintf("http://localhost:%d", mainService.Port), - }) - } - - // Add other services with path-based routing - for _, service := range otherServices { - rules = append(rules, map[string]interface{}{ - "hostname": hostname, - "path": fmt.Sprintf("/%s/*", service.Name), - "service": fmt.Sprintf("http://localhost:%d", service.Port), - }) - } - - // Add catch-all rule - rules = append(rules, map[string]interface{}{ - "service": "http_status:404", - }) - - return rules -} - -func (tm *TunnelManager) UpdateProjectTunnel(projectName string, services []models.Service) error { - // Stop existing tunnel - if _, exists := tm.runningTunnels[projectName]; exists { - err := tm.StopProjectTunnel(projectName) - if err != nil { - return fmt.Errorf("failed to stop existing tunnel: %w", err) - } - } - - // Create new configuration - _, err := tm.CreateProjectTunnel(projectName, services) - if err != nil { - return fmt.Errorf("failed to create updated tunnel config: %w", err) - } - - // Start tunnel with new configuration - _, err = tm.StartProjectTunnel(projectName) - if err != nil { - return fmt.Errorf("failed to start updated tunnel: %w", err) - } - - return nil -} - -func (tm *TunnelManager) ValidateTunnelSetup() error { - // Check if credentials file exists - if _, err := os.Stat(tm.credentialsFile); os.IsNotExist(err) { - return fmt.Errorf("tunnel credentials file not found: %s", tm.credentialsFile) - } - - return nil -} - -func (tm *TunnelManager) GetTunnelStatus(projectName string) string { - tm.mutex.RLock() - defer tm.mutex.RUnlock() - - if tunnel, exists := tm.runningTunnels[projectName]; exists { - _ = tunnel - return "configured" - } - - return "not_started" -} - -// Compatibility function to match existing AWS deployment interface -func (tm *TunnelManager) ProvisionNetwork(projectName string, services []models.Service) (NetworkInfo, error) { - projectURL, err := tm.CreateProjectTunnel(projectName, services) - if err != nil { - return NetworkInfo{}, err - } - - actualURL, err := tm.StartProjectTunnel(projectName) - if err != nil { - return NetworkInfo{}, err - } - - return NetworkInfo{ - ProxyURL: actualURL, - LocalURL: fmt.Sprintf("http://localhost:%d", services[0].Port), - ALBARN: projectURL, // For compatibility - VpcID: "local", // For compatibility - }, nil -} - -type NetworkInfo struct { - ProxyURL string - LocalURL string - ALBARN string - VpcID string -} diff --git a/backend/nvms/log.txt b/backend/nvms/log.txt deleted file mode 100644 index 627745ba41eff7c819530f852570ceaafde6ad7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37952 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 - locate nvms/readme and codebase (Send to Provisioner Route) - * Unmarshal the NVMS(yaml) as an Object and Validate/Process it - * Begin Generating a Resource Plan -> Send to Builder Route - * Build VPC/Network, Configure Security Groups, Setup Load * * Balancers, Go down the line of the Resource Plan/NVMS Object - * Validate Resources and Send Status -> Deployment Module - * Config/Deploy MicroVM(FireCracker), Config Services, Setup - * Monitoring call portfolio route (Repository, Readme, NVMS) - * Analyze Project (Get Details for Prompting, Read Playground Type from NVMS), Pull Templates from Portfolio, Pick appropriate template given args and build and send back. - * Open appropriate connections for playground and rpovide to route, deployed. - */ - - // sec 1 - project, user, err := readBody(w, r) - if err != nil { - http.Error(w, "Error parsing request", http.StatusBadRequest) - return - } - nvmsString, readMeString, codebase, files, err := ProvisionFiles(w, r, project) - if err != nil { - http.Error(w, "Error provisioning files", http.StatusInternalServerError) - } - fmt.Println("Got files") - //ln(response) - - // add new deployment to project - - // NVMS manifest is parsed and validated below (parseNVMSConfig at line 67) - - //fmt.Println("Project: ", project) - - fmt.Println("ReadMe: ", readMeString) - if project.GetDeploys() == nil { - project.CreateDeploys() - } - - deployID := uuid.New().String() - project.AppendDeploy(deployID, models.Instance{ - UUID: deployID, - Name: "main", - Status: "initializing", - Owner: user.UUID, - - Resources: make([]models.AWSResource, 0), // Initialize slice - }) - //fmt.Println("Files: ", files) - //fmt.Println("Codebase: ", codebase) - - nvmsConfig, err := parseNVMSConfig(nvmsString) - if err != nil { - fmt.Println("Error parsing NVMS: ", err) - http.Error(w, "Error parsing NVMS: "+err.Error(), http.StatusBadRequest) - } - project.NvmsConfig = *nvmsConfig - project.Readme = readMeString - // sec 3 - accesskey, secretkey, err := lib.GetAWSCredentials(user) - if err != nil { - http.Error(w, "Error getting AWS credentials", http.StatusInternalServerError) - return - } - - bucket, err := lib.PushToS3(codebase, accesskey, secretkey, project.Name) - - if err != nil { - fmt.Println("Error pushing to S3: ", err) - http.Error(w, "Error pushing to S3: "+err.Error(), http.StatusInternalServerError) - return - } - instance := project.GetDeploy(deployID) - instance.Resources = append(instance.Resources, models.AWSResource{ - Name: "S3-CodeBase Store", - ARN: bucket.BucketARN, - Status: "deployed", - Region: bucket.Region, - ID: bucket.BucketName, - Type: "S3", - Service: "general", - }) - project.AppendDeploy(deployID, instance) - - ServiceInstances := make(map[string][]lib.EC2InstanceInfo) - serviceMap := make(map[string]models.Service) - for _, service := range nvmsConfig.Services { - /* So here we need to deploy 2x services into a vm, for now just a basic ec2, that will then be run with X command and have the following ports opened publicly for it. */ - fmt.Println("Serve") - instances, err := DeployNVMSService(accesskey, secretkey, bucket, service, files) - if err != nil { - fmt.Println("Error deploying service: ", err) - http.Error(w, "Error deploying service: "+err.Error(), http.StatusInternalServerError) - return - } - res := project.GetDeploy(deployID) - var instanceIDs []string - for _, inst := range instances { - instanceIDs = append(instanceIDs, inst.InstanceID) - } - res.Resources = append(project.GetDeploy(deployID).Resources, models.AWSResource{ - Name: service.Name + "-Deployment", - ARN: instances[0].InstanceID, - Status: "deployed", - Region: instances[0].Region, - ID: strings.Join(instanceIDs, ","), - Type: "EC2", - Service: service.Name, - }) - project.AppendDeploy(deployID, res) - serviceMap[service.Name] = service - ServiceInstances[service.Name] = instances - } - fmt.Println("Handling Net...") - - fmt.Println("Service Instances: ", ServiceInstances) - // wind down services since this is for debug. - // await deployment then setup network - // write service Instances to bodyservBody, err = json.Marshal(ServiceInstances) - // run a for loop on service instances, for each await deploy then register services, prov network and finally listener rules. - var instIDs []string - fmt.Println("Waiting for Initialization") - // add short wait - fmt.Println("Initializing") - for name, instances := range ServiceInstances { - fmt.Println("Initializing ids: ", name) - instIDs = []string{} - for _, instance := range instances { - instIDs = append(instIDs, instance.InstanceID) - } - fmt.Println("Initializing: ", name) - err := lib.AwaitInitialization(accesskey, secretkey, instIDs) - - if err != nil { - http.Error(w, "Error Checking init", http.StatusBadRequest) - return - } - - fmt.Println("Intialized: ", name) - } - - alb, vpcId, accessURL, err := lib.ProvisionNetwork(accesskey, secretkey, project.Name) - lbArn := alb.CreateLoadBalancerResult.LoadBalancers.Member.LoadBalancerArn - res := project.GetDeploy(deployID) - res.Resources = append(project.GetDeploy(deployID).Resources, models.AWSResource{ - Name: "ALB", - ARN: lbArn, - Status: "deployed", - Region: "us-east-1", - ID: lbArn, - Type: "ALB", - Service: "general", - }) - project.AppendDeploy(deployID, res) - - project.AccessURL = accessURL - if err != nil { - fmt.Println("Error provisioning network", err) - http.Error(w, "Error provisioning network: "+err.Error(), http.StatusInternalServerError) - } - var listenArn, targetGArn string - // Create listener only for the first main instance - if len(ServiceInstances["main"]) > 0 { - instance := ServiceInstances["main"][0] - fmt.Println("building main listener(s)") - listenArn, targetGArn, err = lib.CreateALBListener(accesskey, secretkey, project.Name, lbArn, vpcId, instance.InstanceID, serviceMap["main"].Port) - - if err != nil { - fmt.Println("Error Creating Listener ", err) - http.Error(w, "Error Creating Listener : "+err.Error(), http.StatusInternalServerError) - } - fmt.Println("SSSListener ARN: ", listenArn) - - res := project.GetDeploy(deployID) - res.Resources = append(project.GetDeploy(deployID).Resources, models.AWSResource{ - Name: "ALBListener", - ARN: listenArn, - Status: "deployed", - Region: "us-east-1", - ID: listenArn, - Type: "Listener", - Service: "general", - }) - fmt.Println("POST APPEND LISTENER", listenArn) - res.Resources = append(project.GetDeploy(deployID).Resources, models.AWSResource{ - Name: "TargetGroup", - ARN: targetGArn, - Status: "deployed", - Region: "us-east-1", - ID: targetGArn, - Type: "TargetGroup", - Service: "main", - }) - project.AppendDeploy(deployID, res) - fmt.Println("Built main listener(s) for instance: ", instance.InstanceID) - } - - priority := 1 - for name, instances := range ServiceInstances { - service := serviceMap[name] - if name != "main" { - for _, instance := range instances { - - tgArn, err := lib.RegisterService(accesskey, secretkey, lbArn, project.Name, name, vpcId, instance.InstanceID, service.Port) - if err != nil { - fmt.Println("Error registering service: ", err) - http.Error(w, "Error registering service: "+err.Error(), http.StatusInternalServerError) - return - } - res := project.GetDeploy(deployID) - res.Resources = append(project.GetDeploy(deployID).Resources, models.AWSResource{ - Name: service.Name + "-TargetGroup", - ARN: tgArn, - Status: "deployed", - Region: "us-east-1", - Type: "TargetGroup", - ID: tgArn, - Service: name, - }) - project.AppendDeploy(deployID, res) - fmt.Println("registered service") - fmt.Println("Listener ARN: ", listenArn) - err = lib.SetListenerRules(accesskey, secretkey, listenArn, tgArn, name, priority) - if err != nil { - fmt.Println("Error creating listener rule: ", err) - http.Error(w, "Error creating listener rule: "+err.Error(), http.StatusInternalServerError) - return - } - priority++ - fmt.Println("Created Listener Rule") - } - } - - } - - if !strings.HasPrefix(project.AccessURL, "http") { - project.AccessURL = "http://" + project.AccessURL - } - fmt.Println("Completed EC2-Deploy.") - fmt.Println("Project: ", project) - if err := project.BeforeSave(); err != nil { - http.Error(w, "Error saving project", http.StatusInternalServerError) - } - err = addToDemo(project) - if err != nil { - fmt.Println("error generating demo: ", err) - http.Error(w, "error generating demo"+err.Error(), http.StatusInternalServerError) - } - projectJSON, err := json.Marshal(project) - if err != nil { - http.Error(w, "Error parsing JSON", http.StatusInternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - w.Write(projectJSON) -} -func addToDemo(project models.Project) error { - reqBody, err := json.Marshal(project) - if err != nil { - return fmt.Errorf("error marshaling project: %w", err) - } - req, err := http.NewRequest("GET", "/generate", bytes.NewReader(reqBody)) - if err != nil { - return fmt.Errorf("error creating request: %w", err) - } - _, err = spinhttp.Send(req) - if err != nil { - return fmt.Errorf("error sending request: %w", err) - } - return nil -} -func DeployNVMSService(AccessKey string, SecretKey string, Bucket lib.S3DeploymentInfo, service models.Service, fileMap []string) ([]lib.EC2InstanceInfo, error) { - instances, err := lib.DeployEC2(AccessKey, SecretKey, Bucket, service, fileMap) - if err != nil { - fmt.Println("Error deploying EC2: ", err) - return nil, err - } - //fmt.Println("Deployed EC2 Instances: ", instances) - fmt.Println("Building Services: ", service) - - return instances, nil -} diff --git a/backend/nvms/projectManager/lib.go b/backend/nvms/projectManager/lib.go deleted file mode 100644 index a0f3f827b..000000000 --- a/backend/nvms/projectManager/lib.go +++ /dev/null @@ -1,79 +0,0 @@ -package projectManager - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "nvms/models" -) - -type ProvisionerResponse struct { - Nvms string `json:"nvmsFile"` - Readme string `json:"Readme"` - ZipBall []byte `json:"zipball"` - FileMap []string `json:"fileMap"` -} - -func readBody(w http.ResponseWriter, r *http.Request) (models.Project, models.User, error) { - var user models.User - var project models.Project - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "Error reading request body", http.StatusInternalServerError) - return project, user, err - } - defer r.Body.Close() - - err = json.Unmarshal(body, &project) - if err != nil { - http.Error(w, "Error parsing JSON", http.StatusBadRequest) - return project, user, err - } - - user = project.User - // sec 2 - if err := project.BeforeSave(); err != nil { - http.Error(w, "Error saving project", http.StatusInternalServerError) - return project, user, err - } - return project, user, nil -} -func ProvisionFiles(w http.ResponseWriter, r *http.Request, project models.Project) (string, string, []byte, []string, error) { - var nvmsString string - var readMeString string - var codebase []byte - var files []string - reqBody, err := json.Marshal(project) - if err != nil { - http.Error(w, "Error parsing JSON", http.StatusBadRequest) - return nvmsString, readMeString, codebase, files, err - } - req, err := http.NewRequest("GET", "/provision", bytes.NewReader(reqBody)) - if err != nil { - http.Error(w, "Error creating request", http.StatusInternalServerError) - return nvmsString, readMeString, codebase, files, err - } - client := &http.Client{} - resp, err := client.Do(req) - if err != nil || http.StatusOK != resp.StatusCode { - http.Error(w, "Error sending request", http.StatusInternalServerError) - return nvmsString, readMeString, codebase, files, err - } - body, err := io.ReadAll(resp.Body) - if err != nil { - http.Error(w, "Error reading response body", http.StatusInternalServerError) - return nvmsString, readMeString, codebase, files, err - } - var response ProvisionerResponse - err = json.Unmarshal(body, &response) - if err != nil { - http.Error(w, "Error parsing JSON", http.StatusBadRequest) - return nvmsString, readMeString, codebase, files, err - } - nvmsString = response.Nvms - readMeString = response.Readme - codebase = response.ZipBall - files = response.FileMap - return nvmsString, readMeString, codebase, files, nil -} diff --git a/backend/nvms/projectManager/parser.go b/backend/nvms/projectManager/parser.go deleted file mode 100644 index ee77af443..000000000 --- a/backend/nvms/projectManager/parser.go +++ /dev/null @@ -1,59 +0,0 @@ -package projectManager - -import ( - "fmt" - "nvms/models" - "strings" - - "gopkg.in/yaml.v2" -) - -func parseNVMSConfig(yamlContent string) (*models.NVMS, error) { - fmt.Printf("Parsing YAML content: %s\n", yamlContent) // Debug log - - config := &models.NVMS{ - Services: []models.Service{}, - } - - // Validate YAML content - if strings.TrimSpace(yamlContent) == "" { - return nil, fmt.Errorf("empty YAML content") - } - - // Parse YAML with error handling - err := yaml.Unmarshal([]byte(yamlContent), config) - if err != nil { - return nil, fmt.Errorf("YAML parsing error: %w", err) - } - - // Validate required fields - if config.Name == "" { - return nil, fmt.Errorf("missing required field: NAME") - } - if len(config.Services) == 0 { - return nil, fmt.Errorf("no services defined in YAML") - } - - // Validate each service - found := false - for name, svc := range config.Services { - if svc.Path == "" { - return nil, fmt.Errorf("service %s missing PATH", name) - } - if svc.Port == 0 { - return nil, fmt.Errorf("service %s missing PORT", name) - } - if svc.Name == "main" { - if found { - return nil, fmt.Errorf("service main already defined", name) - } else { - found = true - } - } - } - if !found { - return nil, fmt.Errorf("service main not defined") - } - - return config, nil -} diff --git a/backend/nvms/projectManager/terminate.go b/backend/nvms/projectManager/terminate.go deleted file mode 100644 index ebd817892..000000000 --- a/backend/nvms/projectManager/terminate.go +++ /dev/null @@ -1,106 +0,0 @@ -package projectManager - -import ( - "encoding/json" - "fmt" - "net/http" - "nvms/lib" - "nvms/models" -) - -func TerminateProject(w http.ResponseWriter, r *http.Request) { - /*Get Project, User from Req -> Deployments from DeploymentsJSON, loop thru call a terminate resource func(analyze service type choose appropriate termination function)*/ - var project models.Project - var user models.User - project, user, err := readBody(w, r) - if err != nil { - return - } - var deployments map[string]models.Instance - err = json.Unmarshal([]byte(project.DeploymentsJSON), &deployments) - if err != nil { - http.Error(w, "Error parsing JSON", http.StatusBadRequest) - return - } - accessKey, secretKey, err := lib.GetAWSCredentials(user) - if err != nil { - http.Error(w, "Error getting AWS credentials", http.StatusInternalServerError) - return - } - remainingDeployments := deployments - for len(remainingDeployments) > 0 { - for deploymentName, deployment := range remainingDeployments { - fmt.Println("Terminating Deployment: ", deploymentName) - deploymentCompleted := true - remainingResources := make(map[string]models.AWSResource) - for _, resource := range deployment.Resources { - remainingResources[resource.ID] = resource - } - for len(remainingResources) > 0 { - fmt.Println("Remaining Resources: ", remainingResources) - for _, resource := range remainingResources { - fmt.Println("Terminating Resource: ", resource.Name) - termCompleted := true - // Try to terminate the resource - err := terminateResource(w, r, resource, accessKey, secretKey) - if err != nil { - // If termination fails, mark deployment as incomplete - fmt.Println("Error terminating resource: ", err.Error()) - - fmt.Println("Keeping Resource") - deploymentCompleted = false - termCompleted = false - - continue - } - if termCompleted { - fmt.Println("Termination Completed: ", resource.Name) - delete(remainingResources, resource.ID) - } - } - } - // If all resources in deployment were terminated, remove it from remaining - if deploymentCompleted { - fmt.Println("Deployment Completed: ", deploymentName) - delete(remainingDeployments, deploymentName) - } - } - } - w.WriteHeader(http.StatusOK) - fmt.Println("Project Terminated") - -} -func terminateResource(w http.ResponseWriter, r *http.Request, resource models.AWSResource, accessKey string, secretKey string) error { - - if resource.Type == "S3" { - err := lib.TerminateS3(resource, accessKey, secretKey) - if err != nil { - fmt.Println("Error terminating S3: ", err.Error()) - http.Error(w, "Error terminating S3: "+err.Error(), http.StatusInternalServerError) - return err - } - } - if resource.Type == "EC2" { - err := lib.TerminateEC2(resource, accessKey, secretKey) - if err != nil { - http.Error(w, "Error terminating EC2: "+err.Error(), http.StatusInternalServerError) - return err - } - } - if resource.Type == "ALB" { - err := lib.TerminateALB(resource, accessKey, secretKey) - if err != nil { - http.Error(w, "Error terminating ALB: "+err.Error(), http.StatusInternalServerError) - return err - } - } - if resource.Type == "TargetGroup" { - err := lib.TerminateTargetGroup(resource, accessKey, secretKey) - if err != nil { - http.Error(w, "Error terminating TargetGroup: "+err.Error(), http.StatusInternalServerError) - return err - } - } - - return nil -} diff --git a/backend/nvms/spin b/backend/nvms/spin deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend/nvms/spin.toml b/backend/nvms/spin.toml deleted file mode 100644 index 086994e63..000000000 --- a/backend/nvms/spin.toml +++ /dev/null @@ -1,57 +0,0 @@ -spin_manifest_version = 2 - -[application] -name = "nvms" -version = "0.1.0" -authors = ["KooshaPari "] -description = "UI Gen Config Parse and Systems builder IAC Platform" - -[[trigger.http]] -route = "/..." -component = "nvms" - -[component.nvms] -source = "main.wasm" -environment = {ENCRYPTION_KEY='t1rSGxkBSy38s8+JNLvHkvJAax4zO/KRsli27VSu6Ks=', SERVICE_KEY="69438bb4a39c6e5ad86678846642a5c6f0b8a0299d467c40d674722e46805bcb"} -allowed_outbound_hosts = ["*://*:*","http://self"] -[component.nvms.build] -command = "tinygo build -target=wasip1 -gc=leaking -no-debug -o main.wasm main.go" -watch = ["**/*.go", "go.mod"] - -[[trigger.http]] -route = "/provision" -component = "provisioner" - -[component.provisioner] -source = "Provisioner/main.wasm" -environment = {ENCRYPTION_KEY='t1rSGxkBSy38s8+JNLvHkvJAax4zO/KRsli27VSu6Ks=', SERVICE_KEY="69438bb4a39c6e5ad86678846642a5c6f0b8a0299d467c40d674722e46805bcb"} -allowed_outbound_hosts = ["*://api.github.com:*", "*://github.com:*", "*://*.githubusercontent.com:*", "*://*.github.com:*","http://self"] -[component.provisioner.build] -command = "tinygo build -target=wasip1 -gc=leaking -no-debug -o main.wasm main.go" -workdir = "Provisioner" -watch = ["**/*.go", "go.mod"] - -[[trigger.http]] -route = "/generate" -component = "demonstrator" - -[component.demonstrator] -source = "Demonstrator/main.wasm" -environment = {ENCRYPTION_KEY='t1rSGxkBSy38s8+JNLvHkvJAax4zO/KRsli27VSu6Ks=', SERVICE_KEY="69438bb4a39c6e5ad86678846642a5c6f0b8a0299d467c40d674722e46805bcb"} -allowed_outbound_hosts = ["http://self","https://api.openai.com","*://*:*"] -[component.demonstrator.build] -command = "tinygo build -target=wasip1 -gc=leaking -no-debug -o main.wasm main.go" -workdir = "Demonstrator" -watch = ["**/*.go", "go.mod"] - -[[trigger.http]] -route = "/build" -component = "builder" - -[component.builder] -source = "Builder/main.wasm" -allowed_outbound_hosts = [] -[component.builder.build] -command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go" -workdir = "Builder" -watch = ["**/*.go", "go.mod"] diff --git a/backend/nvms/tmp/build-errors.log b/backend/nvms/tmp/build-errors.log deleted file mode 100644 index a5c630d8a..000000000 --- a/backend/nvms/tmp/build-errors.log +++ /dev/null @@ -1 +0,0 @@ -exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/backend/odin.nvms b/backend/odin.nvms deleted file mode 100644 index 0ce148d3d..000000000 --- a/backend/odin.nvms +++ /dev/null @@ -1,14 +0,0 @@ -NAME: "BytePort" -DESCRIPTION: "IAC Deployment Platform Powered By Spin and Gin-Gonic" -SERVICES: - - NAME: "api" - PATH: "./backend/byteport" - PORT: 8081 - - - NAME: "nvms" - PATH: "./backend/nvms" - PORT: 3000 - - NAME: "main" - PATH: "./frontend/web" - PORT: 5173 -## set ENV for nvms/api, nvms also needs a buildpack diff --git a/justfile b/justfile index a700e9f4e..65ca6eff0 100644 --- a/justfile +++ b/justfile @@ -39,7 +39,7 @@ go-vet: # gofmt -l . (advisory; CI runs the same) go-fmt: - @unformatted=$(gofmt -l backend/byteport backend/nvms); \ + @unformatted=$(gofmt -l backend/byteport); \ if [ -n "$unformatted" ]; then \ echo "The following files are not gofmt-clean:"; \ echo "$unformatted"; \ @@ -54,7 +54,7 @@ go-lint: # Format all Go in place go-fmt-fix: - gofmt -w backend/byteport backend/nvms + gofmt -w backend/byteport # ----- rust ----- # cargo build --workspace